第5章GPU原理与应用开发 计算机中的图形由点、线、面、体等几何要素和明暗、灰度(亮度)、色彩等非几何要素构成,而图形的生成由图形处理单元(Graphics Processing Unit,GPU)完成。GPU又称显示核心、视觉处理器、显示芯片,是一种专门在个人计算机、工作站、游戏机和一些移动设备(如平板电脑、智能手机等)上做图像和图形相关运算工作的微处理器。GPU的主要任务是对系统输入的视频信息进行构建和渲染,使显卡减少对CPU的依赖。 5.1GPU图形显示体系结构 5.1.1GPU发展过程 1. GPU的起源及发展 nVIDIA公司1999年发布GeForce 256图形处理芯片时提出了GPU(图形处理单元)的概念。GPU可以进行与计算机图形有关的数据运算,而这些功能过去由CPU实现。 随着计算机图形处理技术的不断发展,人们对图形处理的质量和速度要求不断提高。原来单纯依靠CPU计算能力来渲染3D图形的方法已经很难满足要求,用图形处理器GPU来加速图形渲染性能,可以使CPU更专注于通用运算,从而提高系统整体性能。随着Windows等图形界面操作系统的发展,GPU也从最初的简单图形加速发展到图形渲染管线架构,之后又发展为可编程渲染管线,直到目前的统一着色器架构。GPU的发展和图形操作系统及图形应用软件的发展可以说是相辅相成的。 2. GPU的市场及现状 GPU概念提出之前,图形渲染和显示主要依靠图形加速芯片。图形处理器出现之后,国外图形处理器市场先后涌现出很多设计和生产GPU的公司,其中有3dfk、S3、Matrox、SIS、ATI、nVIDIA等。 经过多年的市场激烈竞争和收购兼并,主流桌面级GPU市场基本上被nVIDIA和AMD(AMD收购了GPU巨头ATI)这两家公司垄断,GPU核心技术也被它们控制。 近年来,随着移动互联网的迅猛发展,人们对嵌入式移动设备的要求也越来越高,智能手机、笔记本计算机、便携式数字设备、车载导航系统以及游戏机等设备,对高性能3D图形处理能力的要求也越来越高。为了满足实时高分辨3D图形绘制的需求,嵌入式图形处理器的应用和发展越来越受到重视。 目前嵌入式GPU市场主要有英国Imagination Technologies,英国ARM,美国nVIDIA、Qualcomm、Vivante等公司的产品,这些公司在图形处理器架构设计和研发上都有十分雄厚的技术积累。有代表性的移动图形处理器为nVIDIA公司的Tegra系列、Imagination Technologies公司的PowerVR SGX系列、ARM公司的Mali系列、图芯科技的Vivante GCCORE系列等。 与国外GPU研究相比,国内对GPU的研究起步较晚,技术还处于研发阶段。国内研究GPU的单位有北大众志微处理器研究中心、中国科技大学、电子科技大学、华东师范大学等。国内的GPU有“萤火虫1号”图形处理器、JM5400图形处理器、GP101图形处理器等,性能虽然相对落后,但中国的GPU在崛起,逐步打破国外垄断。 5.1.2GPU硬件结构 1. GPU硬件结构概述 GPU体系结构的变化是一个从简单到复杂的过程。最初,图形显示只是作为一种计算机的输出手段,图形适配器只是其物理实现的媒介。随着图形处理功能在GPU上不断得到增强,图形处理的多种高级功能,如纹理映射等逐渐向GPU转移,CPU图形处理部分得到释放。在现代计算机系统中,CPU通过图形连接器(如PCI/PCIE、AGP插槽等)与GPU进行通信,图形连接器的主要工作就是负责将所有的命令、纹理和顶点数据从CPU传送到GPU。经典GPU硬件架构如图51所示。 图51经典GPU硬件架构 一般情况下GPU应该包括以下功能模块: 2D引擎、3D引擎、显示引擎以及总线接口与存储控制接口。 1) 2D引擎 2D引擎主要完成2D图形的渲染工作。与2D图形处理相关的操作大致可以分为以下几类。 (1) 与位块、画线、多边形及矩形填充相关的操作。 (2) 在一定的时钟周期内处理像素。 (3) 与字体和文本相关的处理。 (4) 实现对硬件光标的加速支持。 2) 3D引擎 3D引擎是图形处理芯片的核心模块,主要用于完成三维图形的渲染,为点、线、三角形、四边形等图元的处理提供3D硬件加速支持,同时支持3D纹理和3D环境映射。这一模块又可以分为两个子模块: 顶点处理模块和像素处理模块。顶点处理模块负责对顶点缓存中的顶点数据进行处理,主要包括对顶点进行平移、旋转、缩放等几何变换以及对图元进行裁剪等操作。 像素处理模块主要完成对图元的逐像素操作。该子模块通常以若干条像素处理管道并行执行的方式来完成这一操作,这样在一个时钟周期内可以同时完成对多组像素的处理。这一子模块可以直接存取像素缓存中的纹理,因此纹理映射也主要在这个子模块中完成。 3) 显示引擎 显示引擎用于完成各种视频格式以及输出接口之间的转换,如常见的最小化传输差分信号、低摆幅差分信号等,同时还支持标准的模拟信号输出,用于支持阴极射线管显示。 4) 总线接口与存储控制接口 总线接口与存储控制接口是图形处理芯片与系统总线(如PCI、PCIE、AGP等)数据交换的中转站。同时,此模块也包括了帧缓冲区接口。 5) 其他功能模块 除上述四个模块之外,GPU中一般还有并行命令处理引擎和消隐模块等。 并行命令处理引擎负责解释CPU发送过来的命令,并根据这些命令选择相应的处理模块。如果是与二维图形处理相关的命令,数据将进入2D引擎进行处理,完成之后进入像素缓存,最后进入帧缓冲区进行显示; 如果是与三维图形处理相关的命令,数据会进入3D引擎进行处理。3D引擎的处理流程相对来说复杂一点: 顶点数据首先进入顶点缓存,然后进入顶点处理模块完成几何变换、裁剪。经过几何处理的图元数据再进入像素处理模块,实现纹理贴图处理以及像素反走样,及消隐处理,最后经像素缓存进入帧缓冲区用以显示。 2. GPU处理器结构 在nVIDIA的GPU里,最基本的处理单元是所谓的SP(Streaming Processor),而一颗nVIDIA的GPU里,会有非常多的SP可以同时做计算; 而数个SP会在附加一些其他单元,一起组成一个SM(Streaming Multiprocessor)。几个SM则会再组成所谓的TPC(Texture Processing Clusters)。 而在CUDA 中,没有TPC 的架构,而是根据GPU 的SM、SP 的数量和资源来综合调整。CUDA处理器实际执行时,会以block(块)为单位,把一个个的block分配给SM进行运算; 而block中的thread(线程),又会以warp(线程束)为单位,把thread来做分组计算。目前CUDA的warp大小都是32,也就是32个thread会被群组成一个warp来一起执行; 同一个warp里的thread,执行的指令相同,处理的数据不同。 基本上warp分组的动作是由SM 自动进行的,会以连续的方式来做分组。例如,如果有一个block 里有128 个thread,就会被分成四组warp,第0~31个thread 会是warp 1,第32~63个thread是warp 2,第64~95个thread是warp 3、第96~127个thread是warp 4。而如果block 里面的thread 数量不是32的倍数,那它会把剩下的thread独立成一个warp; 假如thread数目是66,就会有三个warp: 0~31、32~63、64~65。由于最后一个warp里只剩下两个thread,所以其实在计算时,就相当于浪费了30个thread的计算能力。 一个SM一次只会执行一个block里的一个warp,但是SM 不见得会一次就把这个warp 的所有指令都执行完; 当遇到正在执行的warp 需要等待的时候,就切换到别的warp 来继续做运算,借此避免为了等待而浪费时间。所以理论上效率最好的状况,就是在SM 中有足够多的warp 可以切换,这样在执行的时候,不会有“所有warp 都要等待”的情形发生,因为当所有的warp 都要等待时,SM 就进入空闲状态。 实际上,warp也是CUDA中每一个SM执行的最小单位。如果GPU有16组SM,也就代表真正在执行的thread数目会是32×16个。不过由于CUDA是要透过warp的切换来隐藏thread的延迟、等待来达到大量平行化的目的,所以会用所谓的active thread这个名词来代表一个SM里同时可以处理的thread数目。 而在block方面,一个SM可以同时处理多个thread block,当其中有block的所有thread都处理完后,就会再去找其他还没处理的block来处理。假设有16个SM、64个block,每个SM 可以同时处理3个block,那一开始执行时,device 就会同时处理48 个block; 而剩下的16 个block 则会等SM处理完block后,再进到SM中处理,直到所有block都处理结束。 kepler架构的GK110流处理器簇结构如图52所示,GK110的一个SM有192个Core,因此一次可以同时执行192个线程。SFU(特殊函数单元)用来进行log()、exp()、sin()、cos()等函数运算。LD/ST(Load/Store)用于线程的全局内存或局部内存读写操作。一个SM有192个Core,8个SM就有1536个Core,这么多Core的线程并行执行时需要进行统一管理。GPU每次在1536个Core上执行相同的线程时,就需要知道哪些Core是空闲的。为了能对所有Core做统一调度,从而设计了warp(线程束)调度器。32个线程作为一组(称为线程束)执行相同的指令。一个线程束接收同一个指令,其中32个线程同时执行。不同的线程束可执行不同指令,将不会出现大量Core空闲情况。 图52GK110流处理器簇结构 3. nVIDIA图形处理器硬件架构的改进 2017年,nVIDIA公司发布了Volta V100架构加速器,相比于上一代nVIDIA Maxwell和kepler架构,新架构具备更好的工艺性和更高的运算性能。Tesla V100 GPU一共包含640个Tensor核心: 每个SM有8个核心,SM内的每个处理块有2个核心。在Volta V100中,每个Tensor核心每时钟执行64次FMA(Fused MultiplyAdd,乘法及加法融合指令)运算,一个SM中的8个Tensor核心每时钟周期内共执行512次FMA运算(或1024次单个浮点运算)。 Volta V100流处理器结构如图53所示。 图53Volta V100流处理器结构 新一代的nVIDIA Tesla V100图形处理器硬件架构,相比于5年来的NVIDIA Tesla其他处理器在性能提升如表51所示。 表51nVIDIA Tesla图形处理器性能对比 Tesla产品Tesla K40Tesla M40Tesla P100Tesla V100 GPU型号GK180(kepler)GM200(Maxwell)GP100(Pascal)GV100(Volta) SM数量15245680 TPC数量15242840 FP32核心数/SM1921286464 FP32核心数/GPU2880307235845120 FP64核心数/SM6443232续表 FP60核心数/GPU9609617922560 Tensor核心数/SMNANANA8 Tensor核心数/GPUNANANA640 GPU加速频率/MHz810/875111414801530 FP32/TFLOPS峰值56.810.615.7 FP64/TFLOPS峰值1.70.215.37.8 Tensor/TFLOPS峰值NANANA125 纹理单元数量240192224320 显存位宽/位384/GDDRS384/GDDRS4096/HBM24096/HBM2 显存容量/GB最大12最大241616 L2缓存大小/KB1536307240966144 共享内存大小/KB16/32/489664最大96 寄存器文件大小/KB256256256256 寄存器文件大小/KB384061441433620480 TDP(热设计功耗)/W235250300300 晶体管数量/亿7180153211 GPU芯片大小/ mm2551601610815 制造工艺/nm282816FinFET+12FN 5.2基于GPU图形显示技术 计算机图形显示(Computer Graphics Display)是计算机绘图术语,指计算机所绘图样在屏幕上的显示。计算机绘图一般指在计算机的控制下,从绘图机上输出永久性的图形,适用于静态绘图。如果图形输出设备不是绘图机,而是显示器,则称为计算机图形显示,简称CGD。它既适用于静态绘图,也适用于动态绘图,输出的是供临时观察或实时监视的屏幕图形。 随着计算机科学技术的迅猛发展,借助于计算机的图形显示技术、图像处理技术和模式识别技术均取得了重大进展。在电视节目制作系统中,就有电视字幕机、三维动画工作站和非线性编辑系统等几大应用领域。而在这几大应用领域中,都离不开计算机图形显示技术。 计算机图形显示系统完成平面投影的二维图形或立体三维图形的生成、变换和信息的传送, 这些处理功能需要计算机图形显示系统具备强有力的系统硬件和软件方面的支持。 5.2.1GPU加速渲染管线技术 1. 图形与图像的区别 在计算机图形学领域,图形(Graphic)和图像(Image)是两种不同的表达方式,在处理技术上有很大的区别。图形使用点、线、面来表达物体形状; 图像采用像素点阵构成位图。图形中三角形的顶点与顶点之间是有联系的,它们决定了物体的形状; 图像的像素点之间没有必然的联系。图形的复杂度与物体大小无关,与物体的细节程度有关; 图像的复杂度与物体的内容无关,只与图像的像素点有关。图形放大时不会失真; 图像放大时会产生马赛克现象。图形学主要研究物体的建模、动画、渲染等; 图像学主要研究图像的编辑、恢复与重建、内容识别、图像编码等。 2. 三维图形的处理过程 三维图形的生成与处理过程非常复杂。如图54所示,处理需要经过几何建模→渲染→后期合成等步骤,其中最重要的工作是几何建模和渲染。 图543D图形的生成与处理过程 3. 什么是渲染 渲染(Render)是将3D模型和场景转变成一帧帧静止图像的过程。渲染时,计算机根据场景的设置、物体的材质和贴图、场景的灯光等要求,由程序绘制出一幅完整的画面。3D模型的渲染效果如图55所示。 图553D模型的渲染效果 实时渲染就是图形数据的实时计算和输出。典型的图形数据源是顶点,顶点包括了位置、法向、颜色、纹理坐标、顶点权重等。实时渲染技术广泛用于各个领域,如大型游戏画面、3D实景仿真等,往往采用高性能计算机进行实时渲染。随着GPU处理能力越来越强大,一些实时渲染的图形效果也做得更加逼真与贴切,得到不错的视觉效果。 4. 渲染管线技术 GPU的发展经历了固定渲染管线、可编程渲染管线和统一渲染架构三个时期。 (1) 固定渲染管线也称为渲染流水线。传统的渲染管线由像素着色单元(PSU)、纹理贴图单元(TMU)、光栅化引擎(ROP)三部分组成。从功能上来讲,PSU负责像素处理,TMU负责纹理渲染,ROP完成像素的最终输出。 (2) 可编程渲染管线是用着色器替代固定渲染管线,通过对着色器编程,可以完成以前固定管线无法实现的特效。着色器又可分为顶点着色器(Vertex Shader)和像素着色器(Pixel Shader)。通过对顶点着色器编程可以进行顶点变换、光照处理等操作; 通过对像素着色器编程可以控制像素颜色和纹理采样。GPU在渲染3D图形时,实际上就是对顶点和像素的操作和计算,程序中顶点的数据格式为(x,y,z,w),其中x、y、z为三维空间坐标轴,w也称为RHW,一般情况下w=1,它是为了使处理矩阵的工作变得容易一些; 像素信息为(R,G,B,A),其中R、G、B为红、绿、蓝三色分量,A为Alpha色彩空间,也就是图形的透明度,它是RGB色彩模型的附加信息。着色器编程的目的是改变每一个像素的顶点信息和像素信息,通过大量的渲染工作构建出3D画面和各种特效。 (3) 统一渲染架构的设计思想将顶点着色器和像素着色器合并为一个具有完整执行能力的统一渲染单元(US)。微软公司在DirectX 10中首次提出了统一渲染架构,在统一渲染架构中,指令可以直接对ALU(算术逻辑单元)进行控制,而非过去的特定单元。 5. 渲染管线工作原理 图形适配器的核心部件主要有显示芯片、显示存储器(简称显存)以及RAMDAC(Random Access Memory Digital/Analog Converter),如图56所示。显示芯片处理完的资料会全部传送到显存,然后进入极为关键的RAMDAC单元。RAMDAC单元所需要完成的任务便是数模转换,因为图形适配器芯片处理的是数字信息,而普通的CRT显示器接收的都是模拟信息,所以这一步是必不可少的。RAMDAC的转换速率决定了刷新频率的高低,其转换速率越大,频带越宽,高分辨率时的画面质量越好。 图56图形适配器及相关体系结构 事实上,图形适配器技术发展初期的重点并非是显示芯片,也不是RAMDAC,而是显存。显示芯片与RAMDAC是两个非常忙碌的高速设备,而显存必须随时能够供这两个设备使用。每一次显示屏画面发生改变时,显示芯片就必须更改显存里面的资料,而且这一动作是连续进行的。同样地,RAMDAC也不断地读取显存上的资料,以维持画面的刷新。分辨率越高,从芯片到显存的资料也就越多,而RAMDAC从显存读取数据的速度要更快才行,为此显存必须在容量以及速度方面达到一定的要求。 GPU三维加速图形处理是采用流水线结构来完成的,由顶点着色单元和像素着色单元两部分组成,而像素着色单元更受用户重视。如果从绘画角度来形容两者在加速图形处理过程中所起的作用,顶点着色阶段是在绘画前对整个结构或框架的构思,即将所画的图像轮廓或外框先画出来,到像素着色阶段则对整个框架进行着色,构成完整的三维图像。 几何顶点是GPU流水线处理的最基本的输入数据。而GPU流水线的输出则是存在帧缓存里,输出可以通过数据传输读回内存,也可以通过RAMDAC接口直接在显示屏上得到图像的输出。流水线的第一阶段主要是把顶点由三维的物体坐标转换为屏幕坐标。随后通过光栅化的处理得到很多像素片段从而传入像素处理器并得到最终的图像输出结果。如图57所示,在绘制图像时,GPU首先接收主系统以三角顶点形式发送的几何数据。这些顶点数据由一个可编程的顶点着色器进行处理,该着色器可以完成几何变换、亮度运算等任何三角形运算。接下来,这些三角形由一个固定功能的光栅器转换成显示在屏幕上的单独片元。在屏幕显示之前,每个片元流通过一个可编程的片段处理器运算最终颜色值。 图57GPU渲染管道示意图 6. GPU与CPU的渲染性能对比 1) GPU与CPU的相同点 GPU专用于图形数据的并行计算,对海量图形数据进行并行处理,有极高的计算密度; 而CPU主要专注于通用数据的处理。GPU与CPU有下面共同点。 (1) 它们都是计算机体系结构中的重要组成部分。 (2) 它们都采用超大规模集成电路元件。 (3) 它们都能够完成浮点运算功能。 2) GPU与CPU的不同之处 (1) 它们的设计目的不同。CPU按照“指令并行执行”和“数据并行运算”的思路设计,大部分晶体管主要用于构建控制电路和高速缓存,内部电路复杂; 而GPU的控制电路相对简单,对高速缓存需求较小,可以把大部分晶体管用于计算。 (2) 显存与内存的数据传输延迟不同,显存延迟是内存延迟的1/10。 (3) 显存与内存的带宽不同,显存带宽比内存带宽高出一个数量级。 (4) 线程粒度不同。当CPU线程被中断或者由就绪状态变为等待状态时,系统需要把当前线程的上下文保存下来,并开始读取下一个线程的上下文。这种处理方式使得CPU切换线程所需要执行的时间较长,通常需要数百个时钟周期。而GPU线程是硬件管理的细粒度轻线程,可以实现无延迟的线程切换。 (5) CPU采用多核结构,GPU采用众核结构。CPU每个内核基于MIMD(多指令流多数据流)而设计,每个CPU内核在同一时刻执行自己的指令,与其他内核没有关系。这种设计思想增加了芯片的面积,限制了CPU集成内核的数量。GPU的每个流处理器可以看作类似于CPU的单个内核,每个流处理器以单指令流多线程方式工作,只能执行相同的程序。尽管GPU运行频率低于CPU,但由于流处理器数远多于CPU的内核数(众核),因此GPU的单精度浮点处理能力高于CPU一个数量级。 简单地看,GPU用大量线程实现数据的并行计算,在处理计算量大、线程多、逻辑分支简单的任务时性能出众; 而CPU在处理较为复杂的逻辑运算时有一定优势。 5.2.2GPU图形抗锯齿技术 在提高图像显示性能的同时,提升画面质量也是非常重要的工作。在遇到倾斜的物体表面、弯曲的表面或者是远近景过渡时,很容易出现锯齿现象。抗锯齿的基本方法有: 一是提高图形分辨率,使图形像素尽可能小,但是高分辨率图形增加了显卡的处理负担; 二是采用雾化渲染方法消除锯齿现象,但是这种方法会牺牲图形的清晰效果,或者降低图形的显示速度; 三是将图形边缘像素的前景色和背景色进行混合计算,用第3种颜色来填充该像素(如抖动技术)。 1. 抖动抗锯齿技术 抖动算法是一种应用广泛的图形处理技术,如本书中所有图片的印刷都采用了这种技术,它使用一种颜色(如黑色)的油墨,印刷了多种灰度等级的图片。利用抖动技术进行图形抗锯齿处理时,它通过改变像素的灰度等级排列,得到一种过渡色调,使曲线边缘看起来更加平滑一些。采用抖动技术显示的字符和图像效果如图58所示。 图58文字和图像采用抖动技术进行抗锯齿处理的对比图 抖动抗锯齿技术的基本工作原理如图59所示。假设我们需要在屏幕上显示一根斜线,根据算法生成的理论直线必须以像素点的方式显示在屏幕上。屏幕的最小显示单位是1个像素,如果我们以黑白两种颜色来填充这些像素点(如图59所示),就会在图形边缘产生锯齿现象,使图形边缘看起来非常粗糙。抖动抗锯齿技术的基本原理是利用不同灰度等级的颜色来填充图形边缘,形成图形边缘的过渡色,减小锯齿现象。 图59未采用抖动技术的直线和采用抖动技术后的直线 2. 全屏抗锯齿技术 在利用抖动技术抗锯齿时,我们讨论了利用灰度等级进行锯齿消除。但是灰度等级如何界定、在什么位置采用哪种灰度等级,不同的显卡厂商有不同的实现方法。 目前大部分显卡厂商采用了多级采样抗锯齿(MSAA)技术。MSAA在进行抗锯齿处理之前,首先对于像素的位置进行检测,看图形像素是否位于对象的边缘。MSAA仅处理对象边缘的像素,对于内部像素则不进行处理,从而大大减少了所需要处理的数据量,并且还能保证抗锯齿质量。MSAA根据每个像素中子取样的位置可分为OGSS(顺序栅格超级采样)和RGSS(旋转栅格超级采样)等方法。 OGSS是一种最常使用的抗锯齿取样技术,OGSS是将原来的画面放大,并且在放大的画面中进行2×、4×、16×等采样,然后根据采用点与理论直线的关系决定图形边缘灰度等级的着色操作,然后再将画面还原到原来的大小显示出来,如图510所示。 图510多级采样+OGSS的抗锯齿技术工作原理 3. 子采样点排列方式 像素内部子采样点的排列方式对物体边缘部分着色效果有很大关系。如图510所示,我们以6×(每个像素6个子采样点)抗锯齿为例进行说明。当一条接近水平的分界线穿过这一行像素时,假设分界线下方为黑色(100%灰度),而上方为白色(0灰度)。如果分界线下方没有子采样点则像素的灰度为0,如果所有采样点都在分界线下方则像素灰度为100%,这样每个子采样点占大约16.6%的灰度。如图510所示,随着分界线的上升,可以看到每个像素的灰度等级在发生变化,达到了图形边缘抗锯齿的效果。 全屏抗锯齿(FSAA)就是采用多级采样(MSAA)技术,然后在子采样点排列位置上采用不同方法(如图511所示),实现全屏幕图形的抗锯齿效果。 图511不同子采样点的排列方式 FSAA技术不会给3D画面的色彩或者色阶过渡带来好处,而且对于传统的2D图形显示也没有太大的作用,但是在3D图形应用程序(尤其是3D游戏)中,使用FSAA技术对提高3D画面分辨率非常有效。 5.2.3GPU缓冲区管理 1. 环缓冲区管理机制 图形显示系统中的数据处理采用环形缓冲管理机制,当图形控制器设置在总线主控模式下工作时,主机驱动程序,必须分配一块系统内存作为命令数据包的缓冲区,用于给图形控制器发送命令。这些命令数据包指示图形控制器来完成各种操作,例如在屏幕上绘制对象。这块内存区逻辑上像一个环,它允许数据包以循环的方式被置入或取走,因而命名这个内存块为环形缓冲区。 环形缓冲区是CPU和GPU两个处理器之间协同工作的共享内存空间,被用来实现从主机处理器(写者)到图形控制器(读者)的单向通信。每个处理器都必须保持环形缓冲区的工作状态。这些状态包括如下几种。 (1) 缓冲区基地址: 缓冲区开始的地址。 (2) 缓冲大小: 缓冲区的大小。 (3) 写指针: 主机驱动写入命令数据包的地址。 (4) 读指针: 图形控制器读取命令数据包的地址。 为了使环形缓冲区能够正常工作,两端的处理器必须保持这种状态的一致性视图。缓冲区基地址和缓冲区大小在系统启动后就已经被初始化,从这个点之后就很少发生改变。主机(或者图形控制器)初始化读和写指针以及将其复制到图形控制器(或者主机)相对比较简单。但读指针和写指针在环形缓冲区的操作相当频繁。为了达成一致性,当写者(主机)更新写指针,必须将该值发送给读者(图形控制器)写指针的副本。类似地,当读者(图形控制器)更新读指针,必须将该值发送给写者(主机)读指针的副本。 命令数据包被放入缓冲区中,从起始地址到结束地址,例如,从低地址到高地址。一旦数据放置到结束地址,再一次从头开始放置。与此同时,命令数据包从队列的头部开始执行,其调取方式与它们的放置方式类似。 图512说明了环形缓冲区是如何操作运行的。在该图中,数据包以逆时针的顺序被放入缓冲区,形成一个数据包的队列。在队列中的第一个数据包被标识为P1,最后一个被标识为Pn。两端的读指针指向P1。没有被数据包占用的存储器部分,被称为空闲区域,两端的写指针指向该区域。 图512环形缓冲及其控制机制 最初,读写指针可以同时指向环形缓冲器中相同的位置,例如缓冲区的开始地址。两个指针指向环形缓冲区中的同一位置,通常意味着两种情况之一。其中一种情况是,缓冲区是空的,而另一种情况则是缓冲区已满。为解决两个指针相等的二义性,主机采用环形缓冲区总是保持一个存储单元处于空闲未用状态,避免缓冲区被填满的情况出现。 在主机端,驱动程序将命令数据包写入空闲的环形缓冲区,并通过直接写入到图形控制器写指针寄存器,通知图形控制器有关写指针的任何变动。主机通过比较读写指针来跟踪缓冲区中空闲空间,如果缓冲区(几乎)填满则挂起写入操作。 在图形控制器端,从读指针指向的地址开始,通过PCI/PCIE总线控制器,命令数据包被一个接一个的从队列头部取走,并放置到命令数据包缓冲区。当图形控制器更新其读指针的副本时,它使用一个总线主控写操作来更新主机端读指针的副本,这个副本驻留在一个内存共享的位置。图形控制器有一个寄存器,用来存放主机的读指针所在的内存地址,并使用该地址完成总线主控模式下的写操作。通过比对读写指针,图形控制器同样来跟踪缓冲区中的空闲空间,如果缓冲区为空(即读指针等于写指针),则暂停读操作。 为了减少系统总线上的通信流量,图形控制器不应该在图形控制器端每一次发生变化时都更新主机端读指针的副本。为了方便起见,在数据包队列中,采用了一个双字块的概念。当图形控制器每次都消耗了一个块大小相当的环形缓冲区数据时,将更新主机的读指针副本。除此之外,当图形控制器认为数据包队列为空时会更新读指针的副本。块的大小是可编程的,这允许程序员权衡这两个开销,分别是花费在系统总线上读写指针更新所耗费的通信开销和做实际数据传输的时间量。更大的块大小往往减少通信开销,但是降低了在队列中块的数目,从而降低了主机和图形控制器之间的交互量粒度(或去耦)。 为了减少系统内存总线上的通信流量,驱动程序应该需要尽可能减低访问的频率,以减少对读指针和写指针的复制。为了尽量减少读取读指针,可以检查一次,计算出可用的空间量,而且添加数据包到队列后,可用空间量正在递减。当它看到的自由空间很小(队列几乎全满)时,就可以再次启动这个程序(因为它最后一次读了数据,其副本可能会有所改变)。主机也可以选择,不是每次对数据包队列进行写操作的时候,而是在频率较低的基础上,可能在一个基于块的基础上更新的图形控制器写指针。然而,如果缓冲区为空,更新图形控制器写指针的任何延迟,可能会增加图形控制器响应该命令包的延迟。此外,如果想要图形控制器从队列中读取命令数据包,主机必须小心地更新图形控制器写指针副本,直到队列为空。 当队列几乎被填满时,主机将要轮询读指针,直到有空间可用。在某些系统中,此轮询将驻留在处理器的高速缓存中,从而避免系统总线上的冲突,同时,当图形控制器在总线主控模式下执行读指针的写操作后,主机CPU端的侦听逻辑单元会维持主存和处理器高速缓存之间的一致性。值得注意的是,读指针必须驻留在PCI空间,因为只有这样,该侦听部件才能正常工作。 2. 数据包格式 在命令包模式下编程时,并不需要直接对寄存器进行写操作来进行屏幕绘图。相反,需要做的是在系统内存中准备命令数据包格式的数据,并让固件(微引擎)来完成其余的工作。 目前定义了四种类型的命令数据包。它们是类型0,1,2和3,这里仅列举0型数据包,如图513所示,一个命令包由数据包头和信息主体组成,信息主体紧跟在数据包头的后面。数据包头定义微引擎要进行的操作,信息主体中包含了微引擎中进行的操作所需要使用的数据。 类型0数据包的功能是: 在信息体中写入N个双字,用于写入N个连续的寄存器或特定的寄存器中。 包头字段包含多个控制位: ①寄存器地址索引,相当于字节地址位的[14:2]。因此,它是双字的内存映射地址。字段宽度支持双字地址达到0x7FFF。②保留位,保留供未来扩展的地址空间。③连续标识符,0定义为写入数据到N个连续的寄存器; 1写入所有数据到相同的寄存器。④包体数据数量,信息体中双字的数目。如果信息体中有N个双字,其值应为N-1。⑤分组标识符: 类型0命令为零。包体主要包含数据。 图5130型数据包 5.2.4VxWorks下图形驱动的实现 1. WindML基本结构 WindML(Wind Media Library,媒体库)是风河公司VxWorks操作系统中的图形支持库,主要用来提供基本的图形、视频和声频技术,以及提供一个设备驱动程序框架。 WindML API提供了一个统一的图形硬件接口和设备事件处理能力。 WindML包含软件开发包(SDK)和驱动程序开发包(DDK)两个组件。 SDK组件用来开发应用程序,它提供了一个全面的API(应用程序接口),其负责图形、输入输出、多媒体、字体和内存等方面的处理。 DDK组件用来实现驱动程序,它提供了一个完整的驱动程序参考集,其中包括硬件配置和API,它使得开发者能够迅速地开发自己的驱动程序。 WindML基本结构如图514所示。 图514WindML基本结构 2. UGL图形接口(UGI)API WindML提供以下三种普通驱动程序,可以根据所用的显卡来选择合适的驱动程序。 (1) 16位线性帧缓冲驱动程序。 (2) 8位线性帧缓冲驱动程序。 (3) 基于帧缓冲的普通像素驱动程序。 WindML提供的三种驱动程序只执行了最简单的硬件驱动,如果要发挥显卡的性能,则需要有针对性地编写显卡驱动程序。 开发人员可以通过UGI API来访问图形驱动程序例程,UGI主要接口是一个包含函数指针和不同的数据项的数据结构。函数指针允许访问图形驱动程序。核心ugl_ugi_driver数据结构如下。 typedef struct ugl_ugi_driver { /* Data Members */ UGL_MODE * pMode; /* display mode */ UGL_PAGE * pPageZero; /* First Page */ void * extension; /* optional driver extensions */ /* UGI function pointers */ /* General */ UGL_STATUS (* info) (struct ugl_ugi_driver * pDriver, UGL_INFO_REQ infoRequest, void *info); UGL_STATUS (* destroy) (struct ugl_ugi_driver * pDriver); /* Mode support */ UGL_STATUS (* modeAvailGet) (struct ugl_ugi_driver * pDriver, UGL_UINT32 * pNumModes, const UGL_MODE ** pModeArray); UGL_STATUS (* modeSet) (struct ugl_ugi_driver * pDriver, UGL_MODE * pMode); /* Color Support */ UGL_STATUS (* colorAlloc ) (struct ugl_ugi_driver * pDriver, UGL_ARGB * pAllocColors, UGL_ORD * pIndex, UGL_ARGB * pActualColors, UGL_COLOR * pUglColors, UGL_SIZE numColors); UGL_STATUS (* colorFree) (UGL_DEVICE_ID devId, UGL_COLOR * pColors, UGL_SIZE numColors); UGL_STATUS (* clutGet) (struct ugl_ugi_driver * pDriver, UGL_ORD startIndex, UGL_ARGB * pColors, UGL_SIZE numColors); UGL_STATUS (* clutSet) (struct ugl_ugi_driver * pDriver, UGL_ORD startIndex, UGL_ARGB * pCcolors, UGL_SIZE numCOlors); UGL_STATUS (* colorConvert) (struct ugl_ugi_driver * pDriver, void * sourceArray, UGL_COLOR_FORMAT sourceFormat, void * destArray, UGL_COLOR_FORMAT destFormat, UGL_SIZE arraySize); ... } UGL_UGI_DRIVER; ugl_ugi_driver结构必须为图形设备提供所有的全局数据和函数指针。如果图形设备需要额外的数据项,则需要将此数据项添加到ugl_ugi_driver结构中。程序开发者可以通过写ugl_ugi_driver结构中定义的接口函数来完成图形设备驱动程序的开发。 另一个通用驱动程序结构为ugl_generic_driver,它包含指向普通驱动程序函数的指针和全局数据。 typedef struct ugl_generic_driver { UGL_UGI_DRIVER ugi; /* UGI structure (required) */ /* Device Data */ void * fbAddress; /* Fixed Frame Buffer Address * UGL_MEM_POOL_ID videoMemPoolId; /* ID of video memory pool */ UGL_PAGE * pDrawPage; /* page to which rendering occurs*/ UGL_PAGE * pVisiblePage; /* page visble on display */ /* Generic Driver Data */ UGL_GC_ID gc; /* Active graphics context */ UGL_GEN_DDB * scratchBitmap; /* used for transparent Blts */ UGL_ORD transBitmapCount; /* used for transparent Blts */ UGL_CLUT_STRUCT * pClutStruct; /* color lookup table */ void * pCursorData; /* used for cursor support */ void * extension; /* optional driver extensions */ UGL_BOOL gpBusy; /* GP wait */ /* Generic Driver Routines */ UGL_STATUS (* fbPixelSet) (struct ugl_generic_driver * pDriver, UGL_POINT * pPoint,UGL_COLOR color); UGL_STATUS (* fbPixelGet) (struct ugl_generic_driver * pDriver, UGL_POINT * pPoint,UGL_COLOR *pColor); UGL_STATUS (* hLine) (struct ugl_generic_driver * pDriver,UGL_POS y, UGL_POS x1,UGL_POS x2,UGL_COLOR color); ... 驱动程序中核心函数如下: nxxxDevCreate()、xxxDevDestroy()、xxxInfo()、 nxxxModeSet()和xxxModeAvailGet()。 此外,普通8位线性帧缓冲设备需要以下函数: nxxxClutEntrySet()和xxxClutEntryGet()。 基于帧缓冲像素设备,除了上述所有函数之外,还需要以下函数: nxxxFbPixelGet ()和xxxFbPixelSet()。 3. 显卡设备驱动设计 驱动程序的设计包括以下两部分工作。 第一部分是标准的UGL接口程序,该部分可以从WindML自带的一些驱动程序案例修改而成(如target/src/ugl/driver/graphics目录下的chips或igs等)。 第二部分是显卡的核心驱动程序,这部分通常要处理以下工作。 (1) 查找PCI设备并获取该设备的资源,如帧缓冲区地址、显卡寄存器基地址等。 (2) 初始化时钟、获取内存大小。 (3) 设置相关色度,如8位、16位等。 (4) 获取显示模式(如分辨率、刷新频率等),并检查其有效性。 (5) 配置相关寄存器,设置显示模式及DAC控制器。 (6) 初始化3D引擎,以支持3D功能。 以上工作根据不同显卡设备有所区别,但是处理流程相同。 4. WindML库生成及驱动程序的使用 完成驱动程序开发后,需要进行WindML库及驱动程序的编译工作。 1) WindML编译 WindML库编译的步骤跟通常的编译是相同的,有两种编译方法,即命令行编译和图形编译。下面主要介绍图形模式下的编译过程,如图515所示。 图515WindML配置图 配置主要包括CPU类型选择、颜色模式选择、分辨率设置、键鼠配置以及字体配置等工作。在配置完成后,保存后进行Build编译。编译完成后,在target→lib→pentium→PENTIUM4gnu目录下,会生成两个文件: wndml.o和libwndml.a,将其中一个文件编译到VxWorks工程中即可。 在Tornado2.2开发工具的“Builds”选项中,设置添加库文件到VxWorks工程中,并将wndml.o库文件也添加到VxWorks工程中,如图516所示,然后编译生成VxWorks。 图516Tornado2.2的配置 2) 驱动程序使用 利用WindML自带的example目录下的ugldemo例子程序来测试ugldemo程序首先调用sysAtiPciInit(M1600×1200×60)来初始化ATI显卡,并在文件中包含头文件atiMach64User.h,即#include "atiMach64User.h"。 sysAtiPciInit是核心驱动初始化函数,atiMach64User.h头文件主要是提供给用户的一些接口函数及常数的定义。 以上简要叙述了显卡在VxWorks操作系统WindML下的开发过程,新的显卡驱动需要按WindML架构配置好关键结构体、数据库文件、驱动文件,实现图形驱动的几大要素后,逐步调试完成。 5.2.5Linux下图形驱动的实现 1. Linux下GPU驱动组成 Linux图形子系统由四大部分构成: 一是硬件图形加速设备(GPU)和输入输出设备(如键盘、鼠标、显示器); 二是窗口系统(如XWindow或Wayland); 三是图形加速渲染函数库(如OpenGL或者Direct3D); 四是图形应用程序(如Firefox3D游戏),这些组件共同构成一个计算机图形子系统。 Linux图形子系统的典型构成是将XWindow/Wayland作为窗口系统,Mesa 3D提供高级图形加速渲染编程接口,通常以动态链接库的形式出现。Linux内核的渲染管理器(DRM)提供管理GPU的图形硬件接口。图517描述了Linux图形子系统的组成与各模块之间的关系。 图517Linux图形子系统的基本结构 Linux下的图形驱动不只是一个单独的程序,它包含了从用户态到内核态的多个软件模块,通常由Mesa 3D库为图形开发人员提供OpenGL编程接口,完成驱动用户空间的功能,由内核态的直接渲染管理器负责控制GPU的硬件加速功能。用户态的OpenGL库相当于是基于GPU的一个Mesa 3D库的实现。GPU内核驱动模块相当于内核态的直接渲染管理器。 2. Mesa 3D Mesa 3D是根据OpenGL规范实现的开源图形软件库,它由VMware和Intel等公司共同开发完成。Mesa 3D与OpenGL有相同的应用程序接口,Mesa 3D用软件模拟的方式实现了所有的OpenGL API函数,即使在没有图形硬件加速的系统中,用户也能通过使用Mesa 3D软件库来完成3D图形的渲染过程。Mesa 3D已经被广泛应用在各种平台和图形系统中。Linux下的图形驱动软件不仅仅是一个简单的可执行程序,它是由多个组件相互协作,共同组成的。虽然图形硬件厂商提供的显卡驱动是一个独立的程序,但它本身还是由多个功能组件封装而成,而且通常是几十兆甚至上百兆的庞大程序。Mesa 3D是开源代码库,可以免费获取其源代码来研究和分析,为OpenGL软件库设计提供借鉴。 3. XWindow图形系统 Linux下最多的图形用户界面(GUI)是X窗口系统。XWindow是一个图形工业标准,它有多种不同的实现方式,目前大多数Linux发行版中使用的是Xorg。XWindow采用客户/服务器(Client/Server)模式。无论是本地图形界面,还是远程图形界面,都以同样的流程工作。因此XWindow包含XServer和XClient两部分,其中XServer是XWindow系统的服务器端,XClient是XWindow系统的客户端,XClient通过X协议实现与XServer的交互。 XServer诞生时,UNIX系统还没有共享库的概念,为了减少多个客户端使用同样的库而造成的内存消耗,UNIX系统把大多数与图形相关的代码都放到了XServer中,XServer也被设计成通过网络为客户端提供服务,因此XWindow就成为了Client/Server模式。 XServer只提供创建GUI的基本框架,而把桌面外观的任务留给了桌面管理器,安装不同的Linux发行版(如Fedora和Ubuntu),就会有风格迥异的桌面环境。XServer在处理3D客户程序时比2D程序效率明显降低,因为3D图形应用通常会向GPU传输大量的图形模型信息,而与XServer的通信会造成更大的延迟。为了解决这个问题,DRI(直接渲染结构)技术就应运而生了。 4. DRI/DRM架构 在Linux刚开始支持图形硬件加速的阶段,只有唯一的一个程序可以直接访问显卡,它就是XFree86 Server。XFree86 Server被赋予了超级用户的权限,可以从用户空间直接访问硬件,实现2D加速而不需要内核的支持。这样设计的优点是简单,因为不需要内核组件的支持,XFree86 Server可以很容易地从一个操作系统移植到另一个操作系统。后来,第一个与硬件无关的3D加速技术UtahGLX(基于OpenGL的XWindow)被引入到Linux中,UtahGLX最基本的组件是新增加的一个运行在用户空间的3D驱动,GLX能以XFree86 Server相似的方式,直接从用户空间访问图形硬件。与硬件无关的3D加速设计如图518所示。 图518与硬件无关的3D加速设计 显然UtahGLX模型也有一些弊端: 一是为了完成3D图形加速,它要求没有授权的用户空间程序能够访问图形硬件,这对系统来说并不安全; 二是所有的OpenGL 3D图形绘制都需要间接地由XServer传递大量数据,这会严重影响图形的渲染效率。 为了解决UtahGLX模型的安全性和效率问题,专家们提出了直接渲染模型(DRI)。DRI模型的策略是依靠一个额外的内核模块DRM(直接渲染管理器)来直接访问硬件,并负责检查3D渲染命令流的正确性和安全性。而且3D渲染数据不再需要通过XServer,这样就提高了3D渲染的速度,但是2D渲染命令仍然由XServer交给GPU。这意味着XServer仍然需要运行在超级用户权限下,所以系统安全性仍然存在隐患。其次,这种2D驱动和3D DRI驱动同时访问同一个硬件的设计会带来一些问题,如静态冲突等。为了能解决这些问题,Linux内核开发者又将内核的帧缓冲功能合并到DRM模块,同时让X.Org通过DRM模块访问GPU,这样XServer就可以完全运行在非特级权限下,保证了系统的安全性,并形成了DRM模块中的一个新功能KMS(内核模式设置)。图519说明了Linux的图形驱动栈组织结构。 图519Linux的图形驱动栈组织结构 Linux图形驱动栈中最重要的三个组成部分是XWindow窗口系统、OpenGL实现的Mesa 3D和内核DRM。它们中有的为用户提供图形界面,有的为应用程序提供图形操作接口,有的负责管理图形硬件,这三者共同支持着Linux下的图形子系统。 5. OpenGL图形库架构 OpenGL是一个独立于硬件的程序接口规范。这个接口包含的函数超过700个(OpenGL 3.0函数大约有670个,另外50个函数位于OpenGL工具库中),这些函数可用于指定物体和操作,创建交互式的三维应用程序。在Linux系统中,OpenGL是以一个动态链接库的形式存在的。一般用户可以在系统的/usr/lib(64位系统是/usr/lib64)目录下找到OpenGL核心库文件和它的符号链接: libGL.S0.1.2.0、libGL.So.1、libGL.So。这里OpenGL动态库的来源有两种, 图520Opengl核心库的 总体结构图 一种是由Linux发行版自带的开源OpenGL实现,如Mesa 3D提供的OpenGL动态库; 另一种是由显卡生产商提供的专门显卡驱动。这两种动态库的OpenGL接口都是标准的OpenGL API,但后者的驱动能力更加强大,性能要优于开源的驱动。 OpenGL核心库是OpenGL标准中的基本函数库,它用于实现最基本的、最核心的图形处理功能。它的主要功能有基本图元绘制、矩阵操作、光照材质、显示列表、渲染上下文管理、错误状态的管理与返回、GPU命令的翻译和命令缓冲区管理、用户态驱动与内核态驱动的接口,以及求值器和显示列表等函数的实现。 如图520所示,OpenGL核心库的结构为四层: API管理层、函数预处理层和命令解析层。 6. API管理层的设计和实现 API管理层重要的数据结构是struct dd_ftinction_table,这个结构体称为设备驱动函数表。OpenGL中所有函数都通过这个结构体中的函数指针来访问。这种通过函数表来访问设备驱动的方式有两个主要目的: 一是可以在多个不同的设备驱动之间切换; 二是可以使用渲染状态或帧缓冲配置的优化的函数。在dd_ftinction_table中注册的函数分为强制的和可选的两种类型。强制的函数是指每个设备驱动都必须实现的函数; 可选的函数是指提供特殊硬件和优化算法的方法的函数,它们的实现是驱动程序可选的。 图521是OpenGL核心库初始化阶段的执行流程。首先应用程序在main()主函数中调用glutInit(),glutInit()主要完成窗口属性的设置和初始化操作,最后它调用xuptInit()完成OpenGL上下文所有的初始化操作。xuptInit()完成的初始化操作包括: 创建并初始化FlyContext OpenGL上下文结构、初始化显示列表ctx->DisplayList、初始化“使能标记”数组enable_flag_array[]、初始化光照和纹理的状态等。 图521OpenGL核心库初始化阶段的执行流程 图522函数预处理层流程 7. 函数预处理层的设计和实现 OpenGL渲染的上下文结构体gl_context是一个核心的数据结构,这个结构体十分庞大,它几乎包含了所有与OpenGL状态和渲染有关的项目。OpenGL函数的第一个参数都是struct gl_context *ctx。事实上,在gllib.h头文件中声明了一个全局变量struct gl_context *ctx,它也是传入xupt_gl*这些函数的第一个参数。gl_context可被看作是各种具体设备驱动继承的基类,从它可衍生出面向具体设备的渲染上下文子类。在libflygl库中,自定义的“子类”上下文结构是fly_context。 在OpenGL中,大部分函数都是通过GPU硬件加速实现的。这类函数的预处理层都有以下相似的实现流程。 (1) 获取上下文。 (2) 检査由标准OpenGLAPIgl*()传入的参数是否合法,如果不合法,则进行错误记录、错误报告以及相应的错误处理。 (3) 如果参数合法,就记录或更新上下文对应的状态参数。 (4) 通过驱动函数表,调用命令解析层实现OpenGL功能函数,如“ctx->Driver.Begin(ctx,GL_LINE_STRIP);”这里的ctx->Driver.Begin指向的就是命令解析层的xupt_glBegin(struct gl_context *ctx,GLenummode)。 函数预处理层流程如图522所示。 8. 命令解析层的设计和实现 命令解析层主要是完成对硬件加速处理的GL命令解析,将命令转换成指定格式的硬件指令,并对这些指令进行管理,同时完成与GPU的数据交互,读写GPU内部寄存器。 在GPU内部,所有命令通过对应的指令实现,所有指令都是以二进制形式表示,根据规定格式实现相对应的功能。SoC GPU内部通过一个二进制数表示一条指令,一条指令根据作用可以分成五个部分,分别是: 命令码、控制字、参数1、参数2、参数3和参数4。其中命令码是一条命令区别于其他命令的标志; 控制字完成对该条命令的扩充和说明,用来指示该命令中参数的格式与个数及该命令是否有后继命令; 其他4个参数为该函数所带的参数。一条GL函数根据参数个数不同,可能由1条或1条以上的指令实现,但在同一条GL函数的几条指令中,命令码完全相同。表52是一条指令的具体格式,命令码和控制字分别由10位二进制数表示,最高12位为预留位,没有任何意义,4个参数则分别由32位的二进制数表示。为了简化软件的编程和管理,在软件设计时,命令码和控制字及保留的髙12位共同组成一个32位字。事实上,命令码和控制字对驱动程序是透明的。命令解析层将一条指令按照字节数拆成5个32位的字,在命令解析层通过无符号整型来表示和管理。 表52GPU指令的格式 D4D3D2D1D0 1591481471381371281279695646332310 预留位命令码控制字参数4参数3参数2参数1 图523命令解析层的 命令解析过程 如图523所示,命令解析层对每一条函数的处理方式大致如下。 (1) 检査上下文状态标志,更新相应的计数器。 (2) 对函数中所有参数都进行解析。如果参数类型为枚举类型,则根据GPU的命令编码表对每个参数进行编码翻译; 如果参数不是枚举类型,则将参数值转换成无符号整型值。 (3) 将翻译好的参数值存入指令数组对应位置,如果参数小于4个,则将其余位置设置为零。 (4) 获取函数对应的GPU命令码(包括命令码和控制字),存入指令数组指定位置。 (5) 将指令数组中解析成功的命令存入命令缓存区中。 9. 命令传输层的设计和实现 命令解析层完成检查参数、翻译函数、合成命令的工作后,由命令传输层将命令缓冲区的GPU命令发送给内核层,交给内核驱动,再由内核驱动直接写入GPU硬件的命令处理器FIFO(先入先出)队列。 在Linux操作系统中,运行在用户态的程序不允许直接访问和操作硬件。在Linux操作系统环境下,必须由运行在内核态的内核驱动程序完成最后的命令传输任务。用户空间的命令传输层与内核的数据交换是通过操作系统提供的接口进行系统调用的。这里主要调用了三个系统功能: int open(const char *pathname,int flags); int ioctl(int fd,int cmd,...); int close(int fd); 打开内核驱动创建的设备文件/dev/fly,在完成所有对设备的操作后,程序退出前调用close()关闭设备文件,释放打开的文件描述符。命令传输任务主要由ioctl完成。 OpenGL函数按照是否带有额外的数据可以分为两类,大部分函数经过命令解析层的翻译只会生成1到2条独立命令,只有一小部分函数的参数里带有数据指针。这类函数包括glVertexPointer、glColorPointer、glDrawPixels、glTexSublmagelD、glTexSubImage2D、glTexImagelD、glTexImage2D、glBitmap、gLBufferData。对于该类函数首先将函数参数里的数据指针封装在一个结构体中,这个结构体在后面的内核驱动中同样被使用,用来保存其复制到内核空间的副本。这个结构体的定义如下: struct fly_data{ unsigned int addr_in; unsigned int addr_out; unsigned int size; void *private_data; };/*Reserved,used later*/ 然后再将这个结构体通过ioctl复制到内核空间,由内核驱动根据结构体中数据的指针addr_in将附带数据由用户空间复制到内核空间,完成这个函数的命令传输过程。以glBufferData这个函数为例,这个函数的作用是在绑定了一个缓冲区对象后,保留内存空间以存储数据。它在库中的驱动函数原型如下: void xupt_glBufferData(struct gl_context *ctx,GLenum targer,GLsizeippr size, const GLvoid *data,GL_enum usage); 其含义是分配size字节的内存用于存储顶点数据或索引。data可以是一个指向用户空间的内存指针,也可以是NULL。如果它传输的是一个有效的指针,size字节的存储空间就从用户空间复制到内核空间。具体的实现代码如下: wtruct flu_data arg; arg.size=size; arg.addr_in=data; ioctl(ctx->driverfd,FLY_WRITE_IOCRL,(unsigned long)&arg); COMBINE_GLCODE(n,DATA2BIT(size),(GLuint)arg_addr_out,m,BUFFERDATA); 5.3GPU异构并行计算技术 5.3.1GPU计算体系结构 1. GPU计算概述 GPU最初用于完成图形图像处理。现在GPU已经从由若干专用的固定功能单元组成的专用并行处理器,发展成以GPU内部的通用计算资源为主,固定功能单元为辅的架构,这一架构的出现奠定了GPU计算的发展基础。 GPU主要由流处理器和显存控制器组成。流处理器作为GPU的基本计算单元,与通用GPU的内核相比逻辑电路大大简化。同时,根据应用需求,GPU指令集主要保留了基本算术运算指令,舍弃了复杂的控制逻辑以及多媒体处理指令。 GPU的处理器内核数远高于CPU,如nVIDIA公司的早期GPU产品G80有128个流处理器,而最新Volta架构的nVIDIA Tesla V100拥有5120 CUDA、640个Tensor内核,相对于目前的多核CPU(最多16个内核),GPU可以称为是“众核”结构。 GPU中处理器的数量决定了运算能力; 随着技术的发展,处理器数量还将不断增加,GPU的计算能力将不断增强。GPU通过简化处理逻辑和增加处理单元实现了高效、高性能的并行计算,结构决定了GPU适合于并行计算,其流处理器能够用于大规模并行处理。 为了能使GPU在图形处理功能之外发挥强大的通用计算能力,开发人员提出通用GPU的计算解决方案。GPU利用API接口,将通用计算问题强制转化为图形运算后在图形处理芯片进行计算,这种方法无法使用C、C++等高级语言进行程序开发,编程难度较大,不利于快速掌握开发技术,所以使用范围有限。为此,nVIDIA公司推出了全新的基于GPU的并行计算架构CUDA(Compute Unified Device Architecture,统一计算设备架构),这种架构可以使用C语言进行程序开发,能够直接访问GPU硬件资源。当程序代码需要CPU执行时,由CUDA集成的C语言编译器编译后提交给CPU执行; 程序代码需要在GPU中运行时,由CUDA编译成机器码交由GPU执行。此外,AMD公司也推出基于ATI显卡的GPU计算开发工具Stream。为支持GPU上的程序设计,研究人员已经开发了多种基于GPU的数学函数库,如Matlab等数学工具箱推出了利用GPU加速计算的工具模块。 2. GPU计算的特点和优势 与传统CPU相比,GPU在高性能计算方面有以下特点和优势。 (1) 基于GPU的高性能计算机一般体积较小,可以实现桌面高性能计算,能够满足实验室及野外场地的高性能计算需求,避免了在公共高性能计算机计算时的任务排队。 (2) CPU结构复杂,主要完成控制和缓存功能,运算单元较少。而GPU结构简单,缓存需求少,运算单元丰富,浮点运算能力非常强大,GPU的计算能力远远高于相同数目的CPU。最初CPU和GPU计算能力的差距不是很大。但是在2009年之后,当GPU的性能最终突破了每秒1万亿次大关后,它们的差距就越来越大了。 传统CPU的目标是执行串行代码,这方面它们还是做得非常好。它们包含了一些特殊硬件,如分支预测单元、多级缓存等,所有这些都是针对串行代码的执行而设计的。但GPU并不是为执行串行代码而设计,只有完全按照并行模式运行时才能发挥它的峰值性能。 (3) CPU计算的线程粒度较粗,在并行计算的时间同步阶段需要缓存大量的计算数据,而GPU计算的线程粒度较细,用于并行计算的线程数远大于CPU,在时间同步时GPU的线程通过重复计算来避免缓存数据(缓存速度较慢,影响计算速度)。 (4) 基于GPU的高性能计算机价格相对低廉,维护费用低。 3. 基于GPU架构计算模式 目前,基于GPU的高性能计算主要采用异构架构,即CPU+GPU模式。异构运算能够“使用合适的工具做合适的事情”。CPU在分支处理以及随机内存读取方面有优势,处理串行工作的能力较强。GPU由于特殊的核心设计,在处理大量有浮点运算的并行计算时能力较强。所以异构设计使CPU做串行运算,GPU做并行运算,CPU控制主程序的复杂流程,而将需要批量处理的向量数据通过高速总线传输给GPU,由GPU实现快速并行计算。 通过GPU和CPU的配合使用,可以充分发挥计算机的硬件性能。目前主流的GPU计算开发软件都是采用CPU+GPU的计算模式,目前可以用于异构并行计算的编程环境主要是CUDA、OpenCL和OpenMP,其中CUDA由nVIDIA开发,经过多年发展已经比较稳定,是在nVIDIA硬件平台上进行异构计算的首选。OpenCL是一个开放的标准,已经获得了包括nVIDIA、AMD、Intel和ARM在内的许多硬件厂商的支持。为了支持更广泛的处理器,OpenCL编程相比CUDA要烦琐一些。OpenCL和CUDA的许多概念非常类似,一些完全可以互换使用,因此在两者之间转换并不难,一些公司也开发了源到源的编译器以支持将CUDA代码转化成OpenCL代码。最新的OpenMP 4标准已经加入了异构并行计算的支持,一些编译器厂商正在试图使编译器满足OpenMP 4标准,相信不久之后,GCC等开源编译器都会加入OpenMP 4的支持,这样异构并行计算代码就可以同时在Intel、AMD和nVIDIA的处理器上编译运行。虽然OpenMP不能在某些异构处理器获得高性能,但是OpenCL和OpenMP混合使用能够解决这个问题。 5.3.2GPU并行计算实现原理 1. GPU并行计算架构 GPU内部有大量并行处理单元,这种特殊的设计,在处理大量有浮点运算的并行处理时有天然的优势,这也使得人们开始采用GPU进行通用计算。一个GPU内有许多流处理器簇(SM),它们类似于CPU的内核。这些SM与共享存储(一级缓存)连接在一起,然后又与SM之间的二级缓存相连。数据先是存储在全局存储中,然后被主机取出并使用。除留一部分自己处理外,主机将剩余的数据通过PCIE总线直接送往另一个GPU的存储空间。PCIE总线的传输速度非常快。如图524所示,结点可以在集群中重复设置,通过在一个可控的环境下,重复设置结点就可以构造一个集群。在结点内,多个GPU计算时可能需要相互传输数据,这需要通过PCIE总线进行数据传输,nVIDIA GPU支持GPUDirect P2P技术,通过这种技术在两个GPU之间传输数据时,无须CPU参与,GPU之间的数据交换也无须通过内存中转。GPUDirect P2P技术需要主板的支持,因此各个GPU如何连接到主板上也非常重要。在多个GPU之间进行计算的同时,通过PCIE总线进行数据传输,如果数据传输时间远小于计算时间,那么数据传输时间就可以忽略。 图524集群设计分布图 在单个GPU内,如何合理地安排计算和存储器访问,以最大限度地发挥计算单元和存储器访问单元的效率?如何满足全局存储器的合并访问要求?如何使用共享存储器访问减少全局存储器访问次数?如何合理安排指令顺序,以减少寄存器延迟的影响?在单个GPU内,也需要在计算前通过PCIE总线将数据传到GPU上,在计算完成后将数据传回CPU。一些GPU具备多个数据复制引擎,能够同时进行从CPU到GPU的数据传输,以及从GPU到CPU的数据传输。在GPU内的单个流处理器内,如何使得流处理器上具有足够的线程同时在执行,重叠访存和计算,以计算掩盖访存带来的延迟? 以前GPU计算是通过把通用计算问题转换成为图形计算送到GPU中完成,这使得程序员需要了解OpenGL编程或者DirectX编程,对程序员的要求较高,难以广泛应用。2007年,英伟达的员工发现了一个能使GPU进入主流的契机,他们为GPU增加了一个易用的编程接口,这就是统一计算架构(CUDA)。这样无须学习复杂的着色语言或者图形处理原语,就能进行GPU编程。CUDA将问题分解成线程块的网格,每块包含多个线程。块可以按任意顺序执行。不过在某个时间点上,只有一部分块处于执行状态中。一旦在被调度GPU包含的n个“流处理器簇”(SM)中的一个被执行,一个块必须从开始执行到结束。网格中的块可以被分配到任意一个有空闲槽的SM上。每个线程块内部有很多线程,这些线程以批处理的方式运行,称为线程束(Warp)。而某种意义上来说,CUDA就像指挥官一样,通过使用CUDA,这些程序能够被有效、高效地使用。实际上在并行运算当中,数据是被分为一块一块的同时执行的。运算的结果也是按照一种并行的方式进行表达的。 基于GPU的线程视图如图525所示。 图525基于GPU的线程视图 CUDA是英伟达建立的并行计算平台和编程模型,为开发者提供基于英伟达GPU的开发环境,以便其使用C和C++扩展构建大规模并行应用程序。最新一代的Tensor核心Volta V100加速器架构的改进,进一步增强了CUDA应用程序中并行线程的功能,如图526所示,使CUDA平台的能力、灵活性、生产力和可移植性实现了提高。 图526程序采用显示同步重新收敛线程中的线程 2. 独立线程调度优化 Volta独立线程具有调度功能,可交错执行离散分支中的语句,执行精细的并行计算。 Volta GV100是首款支持独立线程调度的GPU,允许GPU执行任何线程,使程序中的并行线程之间实现更精细的同步与协作。Pascal和早期英伟达GPU均以SIMT形式执行含32个线程的线程组,虽然减少跟踪线程状态所需的资源数量,重收敛线程以最大化并行性,但离散去相同线程束或不同执行状态的线程无法互相发送信号或交换数据,从而产生不一致性。 Volta调度优化器通过在所有线程之间实现等效并发,通过无饥饿现象算法,确保所有线程对争用资源拥有相应的访问权限,将同一线程束中的活动线程一并分组到SIMT单元,以子线程数粒度进行离散和重新收敛,执行相同的代码,避免了上述问题。 3. 多进程服务 Pascal中的基于软件的MPS服务和Volta中硬件加速MPS服务对比如图527所示。 图527Pascal中的基于软件的MPS服务和Volta中硬件加速MPS服务对比 多进程服务(MPS)是Volta GV100架构的一项新功能(Pascal的CUDA MPS是一个CPU进程),专门用于在单一用户的应用程序中共享GPU。 Volta MPS可为MPS服务器的关键组件实现硬件加速,使MPS客户端将工作直接提交至GPU中的工作队列,降低提交延迟并增加总吞吐量(特别是用于高效推理部署),从而提升性能并改进隔离(服务质量和独立地址空间),增加MPS客户端的最大数量,将其从Pascal上的16个增加为Volta上的48个。 4. 协作组 协作组在粒子模拟中的应用如图528所示。 图528协作组在粒子模拟中的应用 并行算法中,线程通常需要通过协作来执行集群计算。构建这些协作代码需要对协作线程进行分组和同步。因此,CUDA 9引入了协作组,用于组织线程组的全新编程模式。协作组编程由以下元素组成。 (1) 为深度学习矩阵算法构建的全新混合精度FP16/FP32 Tensor核心。 (2) 表示协作线程组的数据类型。 (3) CUDA启动API定义的默认组(如线程块和网格)。 (4) 将现有组划分为新组的运算。 (5) 同步组中所有线程的障碍运算。 (6) 检查群组属性以及特定于组的集合运算。 协作组以子线程块和多线程块粒度显示定义线程组,并且可以执行集合运算,让开发者以安全、可支持的方式通过灵活同步功能针对硬件快速进行各种优化。协作组还实现了抽象化,让开发者能够编写灵活、可扩展的代码,该代码可在不同的GPU架构中安全运行,包括扩展至未来GPU功能。Volta独立线程调度也以任意交叉线程束和子线程数粒度,为线程组实现更灵活的选择和划分。 5.3.3CUDA并行计算应用 1. CUDA的技术特征 CUDA采用C语言作为编程语言,提供大量的高性能计算指令开发能力,使开发者能够在GPU的强大计算能力的基础上建立起一种效率更高的密集数据计算解决方案。CUDA的特点如下。 (1) 为并行计算设计的硬件提供统一的软件架构。 (2) 在GPU内部实现数据缓存和多线程管理。 (3) 在GPU上可以使用标准C语言进行编写。 (4) 提供标准离散FFT库和BLAS基本线性代数计算库。 (5) 提供CUDA计算驱动。 (6) 提供从CPU到GPU的加速数据上传性能。 (7) CUDA驱动可以和OpenGL DirectX驱动交互操作。 (8) 能够与SLI配合实现多硬件核心并行计算。 CUDA C在2007年出现以来,许多程序员都尝试以CUDA C为基础构建应用程序。基于CUDA C编写的代码比之前的代码在性能上提升了多个数量级。 2. CUDA并行计算应用 当前各种领域图像采集、处理及解释过程越来越复杂,并且需要通过大量的数据运算进行过程推演,CUDA并行处理功能越来越重要,应用也越来越广泛。 1) 油气勘探地震成像处理 近年来,随着勘探地质目标的逐步复杂化(例如深海勘探)以及高密度全方位采集技术的逐步普及,从野外采集的原始地震数据规模产生了爆炸性的增长,地震勘探正在迈入PB级的“大数据时代”。高速增长的地震数据规模导致地震数据的处理周期越来越长,其中叠前时间偏移是常规处理中最为费时的环节,占据处理周期40%左右的时间。而过长的处理周期难以满足地震资料处理的时限要求,因此提升叠前时间偏移的计算效率迫在眉睫。 图529是采用CPU/GPU架构的异构高性能集群的体系结构。集群中每个计算结点配置了两个CPU,两块GPU加速卡,结点间使用高速网络互联。对叠前时间偏移的并行成像算法进行优化,使之能够充分地利用每个结点的GPU资源以及高速网络。KPSTM并行算法设计的关键是处理好三个并行层次: ①结点间的进程级并行; ②CPU内的线程级并行; ③GPU内的数据级并行。并行计算任务的划分要保证并行层次间的数据局部性以及层次内的可扩展性。同时,不同处理器间的协同计算要尽量保持异步性并充分考虑负载均衡。 图529采用CPU/GPU架构的异构高性能集群的体系结构 图530KPSTM并行算法调度架构 KPSTM的目标是在大规模异构集群上实现高性能,高扩展。因此,算法的关键是做到负载均衡和通信局部性。在并行算法实现上,KPSTM采用了两级Master/Worker架构以调度任务,即将参与计算的结点分为若干组,如图530所示。整个作业只有一个主结点,主结点负责向各个组的主结点分配炮检距成像任务,组长协调组员共同完成该成像任务。在组内采用动态的数据分发方法,即根据结点的计算能力,按需分配输入数据,消除结点间同步操作,最大限度发挥结点的计算性能,实现结点间的负载均衡。 在结点层次上,需要充分处理好CPU和GPU之间的协同计算。如图531所示,CPU与协处理器之间的任务调度与计算结点间类似,CPU与协处理器做同样的炮检距成像任务,地震数据在两类处理器之间动态分发,最后将结果叠加输出。 图531CPU和GPU之间的协同计算 具体实现上,多个GPU协处理器间均分成像面元。在每个GPU处理器内部,对任务粒度进行进一步细分: 每一个线程块分配一部分成像面元,每个面元内的输出地震道由一个线程束的32个线程配合完成,每个线程计算一部分输出样点。GPU内部采用这种任务划分方法,一方面可以产生足够多的任务,另一方面可有利于全局内存(Global Memory)合并访问。通过将输出地震道载入纹理内存(Texture Memory)可进一步加速数据访问性能,这是最为关键的优化策略之一。在实现KPSTM在GPU协处理器的并行算法的过程中,其他的有效优化方法还包括减少每个线程使用的寄存器数量以提高效率、用CUDA流隐藏内存到显存的数据传输延迟等。 图532给出了KPSTM的CPUGPU协同计算加速效果。在该图中,以CPU的单个核的计算性能作为基准。当采用一个GPU协处理器时,加速比达到了36倍多,效率相当于提升了35倍多。而采用两块GPU加速卡时,加速比达到了71倍,计算效率相当于提升了70倍。 图532KPSTM的CPUGPU协同计算加速效果 基于GPU/IB实现的CPUGPU协同计算版本的基尔霍夫叠前时间偏移算法已部署到实际生产环境中。由于其具备良好的可扩展性和强大的计算效率,目前已在中石油油田和地震资料处理基地得到了广泛的使用,并获得了用户的一致好评。 2) 神经网络训练 使用神经网络训练的最大问题是训练速度,特别是对深度学习而言,过多的参数会消耗很多时间。在神经网络训练过程中,运算最多的是矩阵运算,这时正好用到了GPU。采用CUDA构架的GPU以及CUDA编程语言提供基础平台,利用GPU在处理矩阵计算的高效性来实现训练过程的加速。试验证明,在nVIDIA的低端显卡GT720上,通过CUDA构架优化的训练程序,时间可以缩短到7.5ms,比采用Core i7 CPU的运算加速40倍之多,所以GPU对训练过程的加速效果非常明显。 nVIDIA的CUDA C是实现并行计算最成功的编程语言之一。英伟达2017年发布了基于最新Volta架构的nVIDIA Tesla V100,新增了专为深度学习矩阵算法构建的Tensor核心,它是支持大型神经网络训练的关键部件。随着深度学习、大数据和GPU计算的结合,CUDA在并行计算领域的应用前景非常广阔。 5.3.4OpenCL异构并行计算应用 OpenCL(开放式计算语言)先由Apple设计,后来交由Khronos Group维护,是异构平台并行编程的开放的标准,也是一个编程框架。与CUDA只能运行在nVIDIA GPU上相比,OpenCL是一种针对通用并行计算的开放行业标准和跨厂商解决方案,可以实现“一次编写,多环境运行”,大大提高开发效率。OpenCL的设计借鉴了CUDA的成功经验,并尽可能地支持多核CPU、GPU或其他加速器。OpenCL不但支持数据并行,还支持任务并行,同时OpenCL内建了多GPU并行的支持,这使得OpenCL的应用范围比CUDA广泛。 OpenCL包含两个部分: 一是OpenCL C语言(OpenCL 2.1将开始使用OpenCL C++作为内核编程语言)和主机端API; 二是硬件架构的抽象。为了使C程序员能够方便、简单地学习OpenCL,OpenCL只是给C11进行了非常小的扩展,以提供控制并行计算设备的API以及一些声明计算内核的能力。软件开发人员可以利用OpenCL开发并行程序,并且可获得比较好的在多种设备上运行的可移植性。 为了使得OpenCL程序能够在各种硬件平台上运行,OpenCL提供了一个硬件平台层。由于各种不同设备上的存储器并不相同,OpenCL同时提供了一个存储器抽象模型。与CUDA相似,OpenCL还提供了执行模型和编程模型。OpenCL不但包括一门编程语言,还包括一个完整的并行编程框架,通过编程语言、API以及良好的框架结构来支持软件在整个平台上的运行。OpenCL覆盖的领域不但包括GPU,还包括其他的多种处理器芯片。到现在为止,支持OpenCL的硬件主要分布在CPU、GPU、DSP和FPGA上。目前在桌面端和服务器端提供OpenCL开发环境的主要有Apple、nVIDIA、AMD、ARM和Intel,其中Apple提供了一个独立的OpenCL框架并与自家的OSX系统完整地融合在一起; nVIDIA和AMD都提供了基于自家GPU的OpenCL在Windows和Linux上的实现,而AMD和Intel提供了基于各自CPU在Windows和Linux上的OpenCL实现。 异构并行计算已经走出实验室,正在产业界得到越来越广泛的应用,在油气勘探、图像处理、大数据处理和深度学习领域获得重视,并且还在越来越多的新领域生根发芽。 图像处理是GPU的传统优势领域,由于大多数图像处理算法具有数据并行的特征,因此非常适合向量处理器进行向量处理。基于GPU硬件平台,采用OpenCL的异构并行计算已经广泛应用在图像处理领域。 1) 公安视频侦查系统 视频侦查技术已成为新的破案增长点,它已成为了继刑事技术、行动技术、网侦技术之后的侦查破案的第四大技术支撑。随着视频监控系统规模的不断扩大,产生的海量视频数据给视频数据的有效管理和使用带来了新的挑战。如何从海量视频数据中快速检索目标,进行目标特征自动比对,对模糊图像进行清晰化已成为亟需解决的问题。 随着视频监控的发展,视频监控也在不断完善,高清视频已经普及化。高清视频带来了丰富的信息,同时也带来了巨大的数据量,高清视频的编解码也就成了智能化系统的一个瓶颈。由于CPU的流水线工作方式天生不适合并行化和视频数据的处理,如何从海量的高清视频中快速检索目标、自动比对检测对象、快速找出嫌疑目标信息成为一个亟须解决的问题。另外,对于大的视频监控系统中需要并发智能分析处理的视频路数很多,若服务器数量太多布置是个大问题,所以如何在提供尽可能少的服务器的情况下提供更高的处理能力也是关键问题。 通过使用配置了NVIDIA Tesla GPU的Windows服务器,视频侦查系统可以有效地提高视频的解码能力以及视频检索的计算能力。视频侦查系统采用了视频图像处理技术和计算机图形识别技术,运用了运动目标检测、视频浓缩、视频增强、图像清晰化等特定的应用算法。它通过对监控录像的视频图像,进行数字化处理,依据分析检测区域的图像变化,确实视频中的运动目标; 同时对视频进行浓缩和增强。利用OpenCL编程,对运动目标检测、视频浓缩、视频增强和图像清晰化等算法进行优化,并将并行类算法移植到GPU上,可以有效提高整个算法的运行效率,在相同的场景下可以达到10~12倍的速度提升。 视频侦查系统通过使用OpenCL和nVIDIA Tesla GPU实现了大并发量视频的处理,并可以随着系统负载的增加无缝扩展,可快速方便地部署到现有的公安视频监控系统中,快速检索视频中嫌疑目标,提高案件的侦破效率。 2) 三维医学图像处理 CUDA在医学图像三维可视化、三维医学图像配准、三维分割等方面都有广泛的应用。三维分割是医学图像分析和可视化中的重要组成部分,也是医学图像分割的一个难点。水平集方法在三维医学图像分割中有很广阔的应用前景,但是该算法的计算量大,不能达到实时处理的要求。采用nVIDIA公司的GPU和OpenCL编程基础平台,利用图像像素的独立性和偏微分方程求解的并发性,提高水平集算法的分割速度,在保证分割效果的前提下,具有更快的分割速度,实现了快速的三维医学图像分割,满足实时处理的要求。 3) 智慧交通应用 当前交通智能视频处理系统中,当多路视频流传输进搭载Jetson TX2的图形工作站时,深度学习算法可以实时解读视频信息,识别车辆、行人、路牌等信息,甚至还能识别出车辆颜色和品牌型号以及行人的性别、年龄以及手上是否提有物品等信息。整个数据处理过程遵循视频流输入视频流解码深度学习算法等手段识别目标(如车牌、人脸等),并利用OpenCL并行处理完成框选、编码、本地处理等工作,快速存储到云端或显示在监控屏幕上。如果发现可疑车辆或人口,能自动匹配报警,已成为现有高效的安防手段。 大数据处理中需要从海量数据中寻找特殊的模式,这也是异构并行计算的用武之地。许多大数据创业公司使用GPU来分析数据,从中找出有用的信息。 习题5 51简述GPU硬件架构包括的主要模块及功能。 52简述GPU基本的处理器结构组成。 53简述图形与图像的区别。 54简述三维图形的处理过程。 55什么是渲染?