Fast Technology 0-0-作者:赵辉:赵辉[突发新闻]评论(0)收藏文章,
前几个月作者写了一篇文章《风雨二十五载!蓦然回首看ATI显卡发展之路》,详细回顾了AMD(ATI)显卡的发展历程,对更具代表性的显卡和历代重大事件多加笔墨。在写这篇文章的时候,它正处于全盛时期,还处于婴儿期。时光穿梭现在已经部署完毕,颠覆性的APU诞生了。GPU和CPU的传统概念会逐渐融合。作者希望借此机会回顾一下AMD过去统一渲染GPU架构的发展历史,从而窥探AMD GPU架构未来的发展方向。
回到源头:GPU的起源和渲染过程
为什么会有GPU?说实话,这个问题很难回答,但我们还是从过去的一些记忆中得出GPU的概念。虽然民用显卡可以追溯到0世纪0年代,但是当时并没有GPU的概念,所谓的显卡更多的叫显示适配器(),只能用于简单的文字和图形输出,以及是否主要依靠CPU处理D图像和特效。
真正的GPU,也就是(图形处理器)的概念,最早是在年图形处理芯片发布的时候提出的
核心技术包括硬件TL、立方体纹理映射和顶点融合、纹理压缩和凹凸映射、双纹理四像素位渲染引擎等。GPU的出现使得显卡对CPU的依赖程度降低,尤其是在三维图形处理中,取代了原来CPU的一些工作,而这一切都归功于GPU引入的硬件TL(Transformlighting)功能。
三维图形的渲染本质上是由复杂的坐标变换和光源操作组成的。显示卡没有TL功能时,坐标处理和光源操作由CPU处理,称为软件TL。但由于CPU的任务众多,除了TL,还需要做内存管理、输入响应等非D图形处理。所以在实际运行中性能会大大降低,显卡经常要等待CPU数据,运行速度远远落后于复杂D游戏的要求。
硬件TL
GPU从硬件支持TL后,CPU可以从繁重的操作中解脱出来。首先,D模型可以用更多的多边形来描述,所以效果更细腻。其次,CPU不需要计算大量的光照数据,直接通过显卡就可以获得更好的性能。虽然后来GPU取消了TL,采用了全新的ShaderModel来完成D建模和灯光效果,但当时硬件TL技术才是GPU的标志。由于篇幅有限,这里不再赘述更多关于TL的细节。
先说GPU的工作原理。虽然看起来离今天的题目很远,但我认为有必要更好地解释下面的章节。简单来说,GPU的主要功能是完成三维图形的处理,即生成和渲染。一般来说,GPU的图形处理流水线可以分为以下五个阶段:
.顶点处理:在这个阶段,GPU读取描述三维图形外观的顶点数据,根据顶点数据确定三维图形的形状和位置关系,建立三维图形的骨架。在支持DX的GPU中,这些任务由硬件实现的顶点着色器(Top定点着色器)完成。
.栅格化计算:显示器实际显示的图像是由像素组成的,所以我们需要通过一定的算法将上面生成的图形上的点和线转换成对应的像素。将矢量图转换成一系列像素的过程称为光栅化。比如一条用数学表示的对角线,最后转化成一个阶梯状的连续像素。
.纹理映射:顶点单元生成的多边形只构成三维物体的轮廓,而纹理映射完成多边形表面的映射。一般来说,多边形表面粘贴相应的图片,生成真实的图形。TMU被用来完成这项任务。
.像素处理:在这个阶段(在每个像素的光栅处理过程中),GPU完成像素的计算和处理,从而确定每个像素的最终属性。在DX支持后的GPU中,这些任务由硬件实现的PixelShader完成。
.最终输出:ROP(栅格化引擎)最终完成像素输出,渲染一帧后发送到视频内存帧缓存。
所以一般来说,GPU的工作就是完成三维图形的生成,将图像映射到对应的像素,计算每个像素来确定最终的颜色,最后完成输出。
上面我们提到了GPU的起源和渲染过程,TL象征着最初的GPU logo。GPU通过硬件TL实现大量的坐标和阴影转换。随着更加复杂多变的图形效果的出现,对顶点和像素运算的需求大大增加,此时的GPU架构也遇到了一些麻烦的问题。随着游戏屏幕的提高,GPU的要求也越来越高。在通过图形处理生成多边形的过程中,需要添加许多附加操作,如顶点上的纹理信息、映射光源下的像散和颜色表示等。有了这些,可以实现更多的图形效果。
当然,同时也给GPU顶点和像素的计算能力带来了极大的挑战。工程师通过分析GPU图形流水线,发现与传统硬件TL相比,另一种方案的效率和灵活性更高,那就是Shader的出现。00年,微软发布的DirectX带来了ShaderModel,Shader诞生了。
本质上,着色器是一个图形渲染指令集,可以对D图像进行操作,并由GPU执行。
通过这些指令集,开发者可以得到大部分想要的D图形效果。在一个D场景中,通常有很多Shader,有些负责处理D对象的顶点,有些负责处理D对象的像素。所以在最早的ShaderModel.0版本中,根据不同的操作对象使用了VertexShader (VS)和PixelShader (PS)。
相对于TL实现的固定坐标和光影转换,VS和PS有更大的灵活性,使得GPU在硬件上可编程(虽然当时的可编程特性相比现在非常弱),体现在图形特效上的动态光影效果。游戏玩家第一次看到了更逼真、更闪亮的水面;对于开发者来说,开发游戏的难度大大降低。从历史上看,ShaderModel的出现对GPU来说是一场前所未有的革命,未来将成为DirectXAPI的重要组成部分。每次DirectX版本升级,ShaderModel的技术特性都会得到增强和扩展。
具体来说,VS的主要功能是构建D图形的骨架,也就是顶点。本质上,任何三维图形在计算机中都只有两种存在形式,即骨架的顶点和连接顶点的直线。比如我们画一个圆,计算机会把它当成多边形;如果精度低,可能是五边形和/或六边形;精度高的话就是00边或者00边,也就是几百个顶点,几百条直线。
PS的功能比较好理解,主要负责VS后的处理,比如图形表面的纹理,像素值,颜色等等,从而达到想要的效果。
但是,PS分为PixelShaderUnit单元(PSU)、TextureMapUnit单元(TMU)和rasteroperations pipe(ROP;一张卡叫做渲染后台(RBE)。PSU主要负责像素处理,比如我们在游戏中看到的场景和光影效果;TMU主要负责纹理处理,例如树木和石头的纹理,以及水面的反射。ROP/RBE负责像素的最终输出,并执行像素读/写操作、Z-Buffer检查、混色和抗锯齿等。
以上很多单元协同工作,从而形成了ShaderPipeline的概念。
渲染管道也被称为渲染管道。某种程度上,我们可以把它当成工厂里常见的生产线。工厂的生产线是为了提高产品的生产能力和效率,渲染流水线是为了提高显卡的工作能力和效率。当然根据工作类型也可以分为VertexShaderPipeline(主要指顶点单元)和像素渲染流水线(包括PSU、TMU和ROP/RBE),我们常说的渲染流水线指的是像素渲染流水线。
在传统的GPU实时渲染中,一条流水线显然是不够的,因此多流水线并行处理的结构应运而生。一般在相同芯片的比较下,流水线越多,性能越高;在管道数量相同的情况下,新内核的性能高于旧内核。然而,又出现了另一个问题。在传统意义上,像素流水线各部分的单元比例是相等的,但是随着D图形技术的发展,流水线各部分的负载压力开始不平衡。
最简单的例子,VS快速完成顶点处理任务,然后发现PS还在忙。由于计算量大,PS既不能接收VS的新数据,也不能向ROP/RBE输出信号,导致数据延迟。所以PS成了流水线中的瓶颈;在这种情况下,使用更多的PS单元来加强像素和纹理处理成为显卡改进的重点。而PS的数量已经成为衡量显卡性能的标准之一(对于早期的GPU)。
经过以上介绍,我们知道VS和PS是传统GPU中的两个重要指标,那么VS和PS在架构上有什么异同?在计算机图形处理中,最常见的像素数据由三种颜色组成:R、G、B(红、绿、蓝),加上它们共同的信息描述(Alpha),用来表示颜色的透明度,加起来一共是四个通道;顶点数据一般由x、y、z、w四个坐标组成,也是四个通道。所以在架构上,VS和PS既有相同之处,也有不同之处。相似之处在于两者都处理四重数据,不同之处在于VS需要更高的计算精度,而PS的计算精度较低。
其实在渲染D图形的过程中,VS和PS的主要工作就是进行X,Y,Z,W四个坐标运算,通过R,G,B,A的划分来计算像素颜色.为了一次处理一个完整的几何变换或像素渲染,GPU的VS和PS被设计为同时进行四次运算的算术逻辑运算符(ALU)。而数据的基本单位是标量,也就是单个变量,所以GPU的ALU执行这个变量运算一次,称为D标量。
对应标量的是Vector,一个矢量由n个标量和标量组成。因此,传统GPU的ALU在一个时钟周期内可以同时执行次标量并行运算,称为DVector运算。虽然GPU只有一个ALU指令发送器,但它可以同时计算四个通道的数据,这就是所谓的单指令多数据(SIMD)架构。
SIMD的缺点和统一渲染架构的出现
基于以上,SIMD由于先天设计的优势,可以有效提高GPU的矢量处理性能。特别是当顶点和像素为D矢量时,只需要一个指令端口就可以在一个周期内完成预运算,可以00%的效率运行而不浪费计算单元。虽然早期SIMD的执行效率很高,因为许多情况下都是D向量的运算。但是随着D技术的不断发展,图形API和Shader指令中的标量运算越来越多,D/D/D的混合指令出现的频率也越来越高,于是SIMD架构的弊端就显现出来了。渲染架构相关文章最新报道在执行D标量指令运算时,SIMD的效率会下降到原来的/,也就是说在一个运算周期内浪费了/的运算单元。
当面临问题时,ATI和NVIDIA都在寻求改进。进入DX时代后,采用混合SIMD设计,而不是使用简单的D矢量架构,允许矢量和标量指令并行运行(即协发布技术)。比如ATI的R00当时采用了D矢量D标量架构,NVDIA在NV0之后采用了D矢量D标量和D矢量D标量。协同运算技术在一定程度上解决了SIMD体系结构中标量指令执行率低的问题,但当需要分支预测运算时,仍然无法发挥算术逻辑单元的最大运算能力。
除了SIMD架构的缺点,所谓由VS和PS组成的分裂渲染架构也遇到了麻烦。在新一代图形APIDirectX0到来之前,顶点渲染和像素渲染是独立进行的,一旦架构确定,VS和PS的比例就固定了。微软认为这种单独的渲染架构不够灵活。不同的GPU有不同的VS和PS比,极大的限制了开发者发挥的自由空间。另外,不同的应用和游戏对像素渲染和顶点渲染的要求不同,导致GPU计算资源利用率不足。
统一渲染架构:VS和PS负载均衡
比如大型D游戏中很多独立渲染场景,遇到高负载几何工作时,VS处理压力大增,而PS单元工作较少,经常空闲;相反,高负载像素工作时,PS处理压力增大,而VS处于空闲状态。另外,传统的PS和VS曾经各行其是,互不干扰,所以PS帮不了VS,导致GPU执行效率下降。传统的管道架构跟不上时代,导致了DirectX0中UnifiedShaderArchitecture的出现。
所谓的统一渲染架构,是指传统的VS、PS和新推出的DirectX0的GS都是均匀分布的。与传统的GPU架构不同,此时的GPU不再分配单一的渲染流水线,所有的算术单元都可以处理任何着色器操作(无论是顶点操作、像素操作还是几何操作),而这个算术单元往往被称为UnifiedShader (US)。
它的出现避免了传统GPU架构中PS和VS资源的不合理分配,也使得GPU的利用率更高。美国的概念一直沿用到现在。一般来说,USs数量越多,GPU的D渲染执行能力越强,所以USs数量就成为了判断显卡性能的主要标准。
但是对于很多GPU发烧友来说,统一渲染架构的概念第一次接触到的不是桌面显卡,而是00年ATI与微软合作发布的XBOX0游戏主机中使用的Xenos图形处理器。Xenos采用统一的渲染架构,顶点、像素等操作都在US上执行,与ATI以往任何一款GPU在架构上都有所不同。Xenos是ATI的第一代统一渲染架构,对未来的R00影响很大。至于的详细结构,这里篇幅有限,就不多介绍了。有兴趣的读者可以自行参考。
Xenos逻辑架构示意图
第一个统一渲染架构的桌面GPU怎么样?理论上这个GPU肯定是ATI,但毕竟已经有了Xenos的设计经验。但自从00年月被AMD收购后,ATI主要忙于收购后的事宜,研发精力大打折扣。相反,没有统一渲染架构设计经验的NVIDIA在同年月率先发布了G0内核的GeForce00GTX显卡,拉开了桌面统一渲染GPU架构的序幕。G0是NVDIA显卡史上划时代的GPU,其革命性的架构影响了未来的几代显卡。(虽然G0和AMD没什么关系,但是对比下面的G0和R00,可以发现A/N在面对统一架构的时候做出了不同的选择。(
G0的架构变化相当激进。在实现统一着色器的过程中,VS中的D矢量算术逻辑单元和传统GPU架构中的PS被重新设计为功能更加完备的D标量算术逻辑单元。每个alu都有自己专用的指令发送器,所有运算都转换成D标量运算,可以在一个周期内完成乘法和加法运算。NVIDIA将D标量算术逻辑单元称为流处理器。
G0内核架构图
G0采用的D标量流处理器架构称为多指令多数据流架构(MIMD),
完全不同于传统GPU的SIMD架构。MIMD走的是彻底的标准化路线,这种实施的最大优势是灵活性和更高的效率。无论是D、D、D还是D指令,G0都会通过编译器将其拆分成D指令,并发送给不同的SPs进行处理。
这也带来了一些问题。在传统图形处理器中一个周期内完成的D矢量运算在这个标量处理器中只能在四个周期内完成,或者一个D运算需要并行处理四个标量处理器
blink 为什么我们等了这么久
年度人物
上图是浏览器当时渲染的各个线程的任务分布。是多进程架构,上图中的end和end表示不在同一个进程中。线程是内核的主线程,负责解析、排版、执行JS。因为主要是渲染,所以非渲染任务(黄框中)是重点。大多数与渲染相关的任务都在(合成)线程中运行(图中的红色方框代表与渲染相关的任务)。那么早起有什么问题呢?我们先来分析一下这个框架存在的问题:
渲染器线程是一个非常繁忙的线程,浏览器核心的大部分任务都是在这个线程中执行的。光栅化是渲染中非常耗时的任务。如果放在渲染器线程中,会与同一线程中的其他任务争夺运行时间片,相互影响。这样光栅化会比较慢,排版和JS执行的响应会因为渲染任务的插入而延迟。
因为光栅化应该放在渲染器线程上,所以在光栅化之前,有必要将网页分成块,并决定哪些块应该首先去光栅化。也就是说,管理块的功能必须在渲染器线程中。这就导致了一个很麻烦的问题。为了确保快速响应和显示,滑动和缩放处理必须在合成线程中进行。在这种情况下,比如你下滑了一定距离,浏览器需要及时重绘后面的页面块(如果不及时,用户会看到白屏或者白块)。决定光栅化哪些块的逻辑在渲染器线程中。渲染器太忙,所以通常很难等到管理块算法运行并且用户执行下一个操作。或者用户的最后一个操作还没有栅格化,然后他再次执行操作。反正就是各种东西赶不上,总不确定光栅化哪些块。这边渲染器线程举步维艰,另一边排字器线程情况又变了。
还有一点就是页面内容更新产生的脏块和需要用户操作补充的块。应该先画哪个?根据chromium代码中的概念,大型机和implFrame哪个优先级更高?这件事也很纠结,很难判断。
以上缺点带来的不良影响主要体现在用户缩放和滑动场景上。PC上的这些缺点可能是可以容忍的。但是用户在手机上操作频繁,移动终端的性能比PC差很多,所以这些问题比较严重。在chromium开源项目中,为了解决这些问题,开始了渲染架构的调整计划,名为impl-side-painting。顾名思义,把画图,也就是光栅化,放在impl-side的一边。在Chromium的概念中,rendererthread也叫mainthread,compositorthread也叫implthread。也就是说栅格化会移到合成线程。
在介绍impl-side-painting之前,我们先来看看手机终端的安卓平台(苹果在ios上不允许自己的内核,这是IOS系统内核)。安卓.0之后进入硬件加速时代,包括浏览器进入硬件绘制阶段(chrome一直都是硬件加速)。在安卓.x上,另一批来自Googleandroid的人制作了另一个基于WebKit - androidwebview的浏览器内核,这是安卓的系统浏览器。和铬开源项目没关系。当时,安卓系统上自带内核的国产浏览器是基于安卓系统的webview内核进行优化的。与chromium内核相比,该内核代码更简单,渲染架构更高级高效。其渲染任务分布如下: