APP下载

高性能GPU模拟器驱动设计研究①

2020-06-09赵士彭张立志赵皓宇苏孟豪

高技术通讯 2020年5期
关键词:模拟器命令顶点

赵士彭 张立志 赵皓宇 苏孟豪* 刘 苏*

(*计算机体系结构国家重点实验室(中国科学院计算技术研究所) 北京 100190)(**中国科学院计算技术研究所 北京 100190)(***中国科学院大学 北京 100049)(****中国科学技术大学计算机科学与技术学院 合肥 230026)(*****龙芯中科技术有限公司 北京 100190)

0 引 言

目前,图形处理单元(graphics processing unit,GPU)已经广泛存在于个人电脑、移动设备之中。GPU作为图形加速器,在游戏、通用计算、图像处理等领域都起到了十分显著的作用。在现代个人计算机系统中,GPU已经变得越来越不可或缺[1],且变得越来越复杂[2-4]。中央处理器(central processing unit,CPU)[5]不再独立处理图形工作,将需要处理的图形工作转交给GPU执行,从而大幅提高系统的整体性能。GPU一般以插卡的方式,通过主板上的图形加速接口(accelerated graphics port,AGP)或高速串行计算机扩展总线标准(peripheral component interconnect express,PCIe)插槽与CPU进行通信。GPU目前已经逐渐面向通用,通用GPU(GPGPU)[6]已经在通用计算、机器学习、人工智能等相关领域发挥不可替代的作用。当前的GPU支持DirectX、OpenGL、OpenGL ES等应用程序接口(application programming interface,API)。DirectX[7]用于Windows操作系统,OpenGL[8]用于Linux操作系统,OpenGL ES[9]用于安卓和IOS操作系统。这些API都需要GPU的驱动进行处理,转换为GPU硬件可识别的状态和命令等。随着GPU越来越通用,当前的GPU已经拥有了统一的可编程着色器(shader),这使GPU已经成为一种可编程处理器。统一的可编程shader使GPU的可编程性和灵活性大大提高,与此同时,驱动也自然承担起GPU编译器的任务[10,11]。GPU其他功能部件的可配置性也越来越高,这些都需要驱动负责处理。作为CPU与GPU之间交互的桥梁,驱动还负责GPU的任务管理调度等相关工作,这在GPU研发工作中必不可少。目前GPU的驱动多为闭源驱动,各硬件厂商根据自身GPU硬件进行设计。鉴于驱动的代码量巨大,难度很高,本文提出了一种基于Mesa开源驱动框架的驱动设计方法,本方法可以借用Mesa开源驱动,实现模拟器的驱动设计,大幅度减小开发难度并减少代码量。

1 研究背景

1.1 3D图形流水线

3D图形流水线是根据OpenGL、DirectX等API给出的图形渲染流程,它们指导了渲染的每一个步骤。流水线涉及GPU中的渲染算法,描述了从图形API到点,再到图元,最后变为2D图像输出到帧缓冲区的整体过程。首先,从图形API中获得顶点数据流,之后,将这些数据转换为相应的图元,并通过光栅化阶段变为2D像素块用做最终显示。文献[12-14]对GPU的结构和流水线做了更为详细的介绍。

图形流水线的渲染包括以下阶段。

顶点获取顶点获取阶段是从图形API中获取包括顶点的位置、属性以及纹理等相关信息,并将相关信息传递给后面的流水线。

顶点着色器顶点着色器阶段是将顶点的位置、属性等信息通过矩阵运算等方式转换为对应的屏幕坐标和需要的属性,并交给图元组装阶段。

图元组装图元组装将顶点着色器输出的位置信息与图元连接信息对应,将顶点组装成对应的图元,例如点、线或三角形等。并将图元交给光栅化阶段。

光栅化光栅化阶段是将图元变为独立的像素点,即将3D的图元变为2D的像素块,同时计算每个像素块的插值系数,并将这些信息发送给片段着色器。

片段着色器片段着色器为每个像素针对插值系数进行插值运算,使每个像素块都拥有自己的属性等信息。

输出混合输出混合阶段是将每个像素块进行透明度、模板和深度等相干测试,剔除掉不需要显示的像素块。之后经过混合后输出到帧缓冲区进行显示。

1.2 Mesa开源驱动架构

Mesa[15]是一款开源的用户级驱动,经过多年的发展,已经支持OpenGL、OpenGL ES、OpenCL[16]、Vulkan等多种API。在硬件驱动方面,Intel、Nvidia、AMD等厂商的多款GPU已经适配。如今,Mesa已经成为Linux操作系统中使用较为广泛的用户级显卡驱动之一。

Mesa的主要任务是将API的数据及状态处理为硬件驱动需要的命令、状态、指令等。当前的Mesa为了方便驱动开发者对不同的设备进行驱动开发,在原驱动架构中抽象出了Gallium[17]架构。Gallium架构是在原Mesa驱动中抽象出与硬件相关的部分。基于Gallium架构,驱动开发者可无需重复开发与硬件无关的代码,大量减少了开发驱动的代码量及开发难度。

目前,Mesa驱动的架构主要分为3大部分,即API层,硬件层和系统层。图1是Mesa开源驱动架构的结构图。API层是Mesa驱动中负责处理API接口的位置,包含Mesa和state tracker两部分,这两部分的实现均与硬件无关。经过API层后,API的数据和状态会被转换为硬件层中与硬件无关的接口。硬件层的主体是Gallium架构。经过了硬件层的处理,需要渲染的数据和状态已经转换为硬件可识别的命令、状态和指令等。这些数据会经由系统层与内核级驱动(direct rendering manager,DRM)交互,将有关数据传入CPU与GPU的共享内存上。这样,GPU就可以通过命令处理器(command processor,CP)获取需要的数据。

图1 Mesa开源驱动架构

2 高性能GPU模拟器

本文使用的高性能GPU模拟器主要由命令处理器(command processor,CP)、全局任务调度器(global task scheduler,GTS)、图形处理集群(graphics processing cluster,GPC)、二级静态缓存(L2 Scache)、内存控制器(memory controller,MC)5部分组成。其中图形处理集群又由计算处理引擎(compute engine,CE)、几何处理引擎(geometry engine,GE)、图元处理引擎(primitive engine,PE)、局部任务调度器(local task scheduler,LTS)、流处理器集群(stream processor cluster,SPC)、输出合并单元(output merge unit,OMU)6部分组成。整体结构如图2所示。

图2 高性能GPU模拟器的顶层结构

本文使用的高性能GPU模拟器是一个由C++语言编写的GPU模拟器。其设计目的是对gsgpu结构设计做功能验证与一定程度的性能分析,该结构后续还将使用现场可编程门阵列(field progrmmable gate arry,FPGA)进行更深一步功能验证,最终流片,所以在整个GPU设计计划中,模拟器只是一个阶段性产物,而不像ATTILA模拟器作为最终实验平台,所以简单高效地实现GPU模拟器是当前计划中很重要的标准。GPU模拟器整体软件架构由Module与Queue 2个数据结构搭建。Module构建每一个功能模块,类似于Verilog语言中module模块。Queue构建各个module间通讯模块,同时实现各module的解耦合,Queue本质是一个每周期一写一读的循环队列。

3 高性能GPU模拟器驱动

3.1 模拟器驱动架构

高性能GPU模拟器的驱动需要为模拟器提供数据、命令、状态以及shader需要的指令等信息。这些信息是驱动从API中获得并通过处理后得到的。驱动的设计共分为3个阶段。首先,API经过Mesa的API层转化为Gallium架构下与硬件无关的状态,同时将OpenGL着色器语言(OpenGL shading language,GLSL)转化为TGSI(Tungsten graphics shader infrastructure)[18]中间语言;然后,硬件层将硬件无关状态转化为与硬件相关的状态,同时产生硬件执行所需的命令等信息,并将TGSI中间语言转化为GPU模拟器的汇编;最后,将每一帧的信息处理为硬件需要的可读的接口信息,同时将信息传入模拟器的显存中等待执行渲染。

驱动设计中,Mesa的硬件层是设计的重点部分,硬件层的设计也同时指导了模拟器接口的设计,对模拟器的设计起到了指导作用。Mesa的API层是与硬件无关的部分,是可重用的部分,所以在设计中采用了API层的结构。Mesa驱动的系统层中与DRM交互的部分,在GPU模拟器中也无需实现,所以没有采用。

3.2 硬件驱动设计

硬件驱动的设计主要是在Mesa驱动的Gallium架构下实现的。在Mesa的API层中,API的数据及状态已经转换成了Gallium架构中与硬件无关的接口。在硬件驱动的设计中,需要考虑绘制进入接口、状态对象接口、缓存及中断接口和刷新接口等。

首先,驱动为GPU模拟器中的命令处理器准备命令队列,命令队列是用于控制GPU模拟器执行整体渲染的一组命令。驱动为GPU模拟器准备的命令队列设计为draw、sync、dump和halt 4个主要命令。

Draw命令是一条渲染命令,也是整个渲染的核心命令。当执行draw命令时,GPU模拟器会调用已经准备在显存中的draw_info、pipe_state和shader_state等一些必要的状态。命令处理器准备好各状态后,会将顶点、流水线状态等信息通过全局任务调度器逐个发给几何计算引擎。几何计算引擎执行顶点子块处理任务后,LTS 做SPC资源管理,查找合适的SPC,并把线程组任务发过去。然后,SPC执行顶点着色器(vertex shader,VS) 程序,GE 跟踪VS 的位置输出,按原始顶点顺序对应的微块顺序,读出顶点位置数据,并进行广播。之后,PE对图元进行光栅化,LTS 根据资源情况,查找合适的SPC,把线程组任务发送过去,SPC执行片段着色器(fragment shader,FS) 程序。最后,PE将排完序的片段结果送到OMU,OMU进行后处理。

Sync命令是一条同步命令,用于同步GPU模拟器中的各个模块。在整条流水线中,各模块之间会依次传递一个last状态。当模块收到last状态,则说明本次draw_call渲染已经结束,模块会向命令处理器返回已经处理结束的信号。当执行sync命令时,命令处理器会进入等待状态,不会继续执行命令队列中的其他命令。只有当收到所有模块处理结束的信号后,才继续执行命令队列中的其他命令。

Dump命令是一条显示命令,用于将已经渲染完成的draw_call显示出来。当命令处理器执行dump命令时,命令处理器会调用显存控制器,将显存中帧缓冲区渲染好的像素显示到屏幕上。

Halt命令是停机命令,用于停止GPU模拟器的所有工作。当命令处理器执行halt命令时,整个GPU模拟器将停止所有模块的执行,GPU模拟器进入停机状态。

其次,为了使几何计算引擎和顶点着色器准确获取顶点和属性信息,驱动需要配置与顶点和属性相关的状态。Draw_info是驱动配置的一次draw_call的相关信息,包括:起始顶点start,用于索引提前准备在显存顶点区域的第一个顶点位置;本次draw_call的顶点数量count,用于索引显存中顶点区域用于本次渲染的顶点。每个图元的顶点数,用于图元处理引擎进行图元组装;index_buffer用于顶点使用索引缓冲的情况下进行顶点在显存中位置的描述。

为了增强GPU模拟器固定管线的可配置性,驱动需要配置相应状态给对应的固定管线模块。这些配置信息就是API层传下来与硬件无关的状态。这些状态的接口是Gallium架构中的pipe层。在高性能图形GPU中,有2个主要的固定管线,一个是图元处理引擎,用于图元的组装以及光栅化;另一个是输出合并单元,用于像素的测试和混合。本设计将pipe层的状态处理为供图元处理引擎使用的光栅化及裁剪等状态,以及供输出合并单元使用的深度测试状态、模板测试状态和混合状态。这些状态被转换成32位的二进制,传入GPU模拟器的显存中。

Rasterizer_state是用于PE使用的状态,包含了光栅化所需要的点大小、线宽、多重采样等所有配置信息。viewport_state和clip_state也是用于PE使用的状态,告知PE视口的大小以及裁剪的大小,以便光栅化使用。Alpha_state、depth_state和stencil_state是OMU使用的状态,分别用于OMU的透明度测试、深度测试和模板测试。驱动将对应的测试算法告知OMU,由OMU调用深度和模板的缓冲完成所有测试,3种测试分别有7种不同的测试算法可以配置。Blend_state也是OMU使用的状态,驱动将混合算法告知OMU后,OMU调用渲染目标(render target)进行混合后,将结果写入帧缓冲区(framebuffer)等待最后的显示,其中,混合共有19种不同算法。

为了进一步提高GPU模拟器的可编程性,GPU模拟器采用了统一的可编程shader。这就需要驱动为可编程的shader准备相应的状态以及所需寄存器个数等信息。本设计中,shader_state对标量通用寄存器和向量通用寄存器的数量等信息进行了描述。这些信息由驱动根据shader执行的汇编程序及其状态产生。

3.3 编译器设计

随着GPU的可编程性越来越高,驱动中对于GPU编译器的设计也越来越重要。在Mesa驱动架构中,Mesa提供了GPU编译器的前端代码。通过Mesa驱动中的编译器前端,可以将GLSL等编程语言翻译为一种叫TGSI的中间语言。TGSI语言是Mesa为所有的GPU提供的唯一中间语言。本设计主要针对TGSI的中间语言进行进一步的编译器后端设计。表1中是一个彩色正方形程序,通过Mesa的API层编译成的TGSI中间语言。表中,VERT代表当前是顶点着色器对应的程序,PROPERTY NEXT_SHADER FRAG代表对应的下一个shader程序是片段着色器程序。DCL声明了有2个输入,IN0是顶点位置信息,IN1是顶点属性信息。同时还声明了2个输出OUT0、OUT1,一个常量缓存CONST0和一个临时缓存TEMP0。之后进行4×4的矩阵乘法,MUL表示乘法运算,MAD表示乘加运算,之后将结果输出到OUT0。IN1直接输出给OUT1。基于Mesa框架,模拟器驱动将TGSI中间语言根据语法语义进一步编译为GPU模拟器的汇编代码。

表1 正方形在Mesa中翻译为TGSI中间语言

表2是将TGSI中间语言的4×4矩阵乘法部分转换为GPU模拟器汇编后的代码部分。这部分是将原TGSI的MUL和MAD构成的矩阵乘法变为GPU模拟器的汇编语言,可编程的shader将执行这段汇编程序完成矩阵的乘法运算。其中MUL是进行乘法运算,MAD是进行乘加运算。S代表标量通用寄存器,是从常量缓存CONST0取出的数据,是驱动准备好的4×4矩阵。V代表向量通用寄存器,是顶点的位置信息,由输出传入。

表2 4X4的矩阵乘法翻译后的GPU汇编

3.4 模拟器的驱动接口设计

当所有数据、状态、命令和shader指令准备好之后,都将传入到显存之中,以便GPU模拟器根据命令队列进行执行。根据前述的驱动设计,本文提出了高性能GPU模拟器的接口设计,用于指导模拟器的结构设计。该接口可将每一帧中驱动写入GPU模拟器的所有信息变为可读信息,更加方便了后续的调试。同时该接口还可对驱动传入GPU模拟器的信息进行修改,也可提高调试模拟器的灵活性,更加方便地找出问题所在。

该接口将所有信息分为命令、绘制和数据3大部分。命令部分反映GPU的命令处理器中命令队列,是GPU流水线的执行状态。绘制部分包括draw_info、pipe_state和shader_state 3部分。draw_info反映的是模拟器中GE的执行状态,pipe_state反映的是PE和OMU的执行状态,shader_state反映的是模拟器中SPC的执行状态。数据部分则包括了顶点数据、纹理数据和shader指令等。Shader的指令在显存中是二进制的形式存在,本接口还将指令进行了反汇编,更加增强了调试者的可读性。

通过这些可读的信息,在调试模拟器的过程中可以更加方便地知道流水线中各级的执行状态。这些信息以draw_call为单位,每次可显示一帧的数据。同时,通过修改这些接口信息,也可以直接改变模拟器的执行状态,更加方便调试人员调试模拟器。

4 实 验

本实验使用的操作系统为Fedora 28.X86_64,采用的CPU为Intel Core i7 3770,主频3.4 GHz。GPU实验平台采用一种高性能GPU模拟器,Mesa版本为18.0.5,可支持图形API的OpenGL ES 3.0版本。

实验所用基准测试集为GL_mark。在Linux操作系统上,GPU的图形基准测试集并不多。GL_mark是由Linaro发行的一款图形基准测试集,使用OpenGL-ES进行开发,提供了一系列丰富的图形测试,涉及图形单元性能的各个方面,涵盖光照、阴影、超多图元、简单2D等多种类型的测试,是目前Linux操作系统上较为全面的测试集之一。

图3是实验的测试步骤,Mesa开源驱动将GL_mark基准测试集所使用的OpenGL-ES的API转化为相应的中间状态,同时将GLSL的shader编程语言转换为TGSI中间语言。GPU驱动将中间状态及中间语言转化为GPU模拟器的接口信息和底层汇编语言传递给GPU模拟器驱动。经过GPU模拟器的一系列处理,最终以帧的形式显示出来。

图3 GPU驱动验证的实验过程

本文在GL_mark中选择的基准测试描述如表3所示,从GL_mark基准测试集中选择了4个阴影测试,分别用于测试2D显示、反射及阴影测试、折射测试以及复杂shader测试。Draw_call数量包括清除深度缓存和模板缓存、多重采样等操作。图4所示是2D显示测试的效果图。2D显示时,顶点数为4个,即正方形的4个顶点。图中图案是使用纹理贴图实现的。通过该测试表明GPU模拟器可以利用3D图形驱动配合3D图形模拟器完成2D的显示。图5所示是反射及阴影绘制的测试,通过该测试表明GPU模拟器驱动可准确完成反射及阴影的绘制。图6所示是折射绘制的测试,通过该测试表明GPU模拟器驱动可准确完成折射的绘制。图7所示是复杂shader的测试,通过该测试表明,利用GPU模拟器驱动中的编译器可以正确绘制复杂的shader。测试中,2D图形绘制顶点及图元数目较少,每帧虽需3次drawcall,但包含了clear等操作,实际绘制1次drawcall即可完成,具有很好的图形绘制加速效果。其余测试用例顶点及图元数目较多,每帧drawcall数量也相对较多,说明本驱动配合GPU模拟器可准确无误地完成复杂图形绘制。

表3 选取的基准测试集

图4 Effect 2D GPU模拟器效果图

图5 Shadow GPU模拟器效果图

图6 Refract GPU模拟器效果图

图7 Jellyfish GPU模拟器效果图

5 结 论

GPU驱动是GPU与CPU之间交互的桥梁,在GPU开发中起到了不可忽视的作用。本文在Mesa开源驱动的框架下进行GPU的驱动开发工作,可以大幅减少GPU驱动的开发时间和开发难度。同时,为了提高GPU模拟器的可编程性,在Mesa驱动中集成了编译器的设计,可将Mesa中的通用中间语言TGSI编译为GPU模拟器的汇编语言。本驱动还适配OpenGL等多款API以及GLSL等shader编程语言。并且,根据Mesa开源驱动驱动,设计出一套完整的可读GPU模拟器接口,方便调试人员进行驱动及GPU结构的调试工作。通过设计GPU的驱动和模拟器的接口,对高性能GPU模拟器的结构设计起到了指导作用。在GPU模拟器中设计的各个模块均参考该套接口,在本接口的基础上进行结构设计。本设计已经和GPU模拟器进行了OpenGL等API的验证,在GL_mark的验证测试中,可以正常完整地绘制出GL_mark测试集的每一帧图像。本接口设计后续可继续用于高性能GPU的结构设计中,驱动设计后续还可继续用于GPU的用户态驱动中,通过实现内核态驱动DRM,并编写libDRM接口与本设计进行对接,便可实现高性能GPU的一整套完整驱动。

猜你喜欢

模拟器命令顶点
只听主人的命令
过非等腰锐角三角形顶点和垂心的圆的性质及应用(下)
过非等腰锐角三角形顶点和垂心的圆的性质及应用(上)
了不起的安检模拟器
盲盒模拟器
划船模拟器
移防命令下达后
这是人民的命令
动态飞行模拟器及其发展概述
数学问答