OpenGL Insights 阅读有感 - Tile Based架构下的性能调校 翻译

Performance Tunning for Tile-Based Architecture

Tile-Based架构下的性能调校

 

by Bruce Merry

GameKnife译

 

 

译序                       

在大概1个月以前,花了两个小时的时间阅读了OpenGL Insights上的两篇关于移动平台GPU的优化文章。当时正巧在公司做移动渲染器的优化和整理,顿觉醍醐灌顶。同时搜索国内关于这方面的经验文正或者翻译文章,感受少之又少。因此,萌发了翻译这两篇文章的想法。其实也是第一次作翻译工做,本觉读两篇文章就用两个小时,翻译大概一天就够了把。 结果第一天写了一下午,也就翻译出了一个开头,发现这不是一件容易事,所以干脆就慢慢来翻译。程序员

终于,一个月以后,总算翻译到了最后一章。全文翻译下来,才发现第一次阅读其实读漏,读错了不少东西,同时,在开发实践的过程当中,又对文中的不少章节有了更深入的理解。所以在某些地方加上了本身的译注。算法

最后,把这篇译文分享出来,但愿能给在移动GPU平台开发图形程序的朋友一些帮助。编程

 

引言                      

OPENGL 和 OPENGL ES标准描述了一种虚拟渲染管线,这个管线将三角形的处理规定为以下流程:缓存

  1. 变换三角形的顶点
  2. 对变换后的顶点进行光栅化,生成像素
  3. 对像素进行着色,写回帧缓存(framebuffer)

而后按照这个流程进行下一个三角形的处理,如此往复。然而,这对GPU的工做来讲,并非一个高效的流程,GPU会常常的重排和并行化这些过程以达到更高的效率。安全

在这篇文章中,咱们会详细阐释tiled-based渲染方法,tiled-based架构在主流的移动平台GPU上是一个一般的流行图形管线组织方式。咱们会关注:什么是tiled-based渲染,为何要用它,以及咱们须要作些什么(注: 相对于传统的当即渲染方式)来达到效率的优化。性能优化

咱们假设阅读者是这样的人:数据结构

  • 已经拥有优化OPEN GL程序的经验,熟悉标准优化技术,例如减小状态切换,减小DRAWCALL,下降shader复杂度以及纹理压缩。
  • 想要获得关于tiled-based GPU上的优化建议。

有一点,须要牢记于心:每个GPU, 每个图形驱动, 每个程序都是不一样的,具备不一样的性能特征。基本上,性能调校是一个不断分析和实验的过程。所以,这个本文几乎不会提供“立竿见影”的金科玉律,可是会尝试阐释出如何经过不一样的手段来估算性能开销,最终帮助读者选择优化方法。架构

本文是主要是介绍如何最大化程序性能的,但因为tiled-based GPU是移动设备的主流,咱们也会提到一些电量消耗的控制。大多数桌面程序的目标是简单的想尽一切办法在一秒内渲染更多的的帧,始终100%的消耗可使用的电能(注:俗称跑满)。而在移动平台上,谨慎的将帧率限制在一个合适的水平,能够节省电能会有效的延长电池寿命,同时会相对的提升用户体验。固然,这并不意味着咱们能够在达到了目标帧率后就能够放弃优化:越多的优化会给系统更多的休眠时间,这会更加节省电能。异步

最后,这篇文章的内容主要聚焦在OPENGL-ES上,由于这是tiled-based GPU的主要市场,咱们会偶尔提到桌面OPENGL特性以及他们是如何运转的。工具

 

背景                    

桌面GPU的主要目标是最高性能。而移动GPU则不一样,它必需要平衡性能和电能的消耗。设备中电量开销最大的消耗者之一就是内存带宽,相对来讲,计算比缓存更加经济高效,可是计算也会产生一些临时数据,越多的数据被抛弃掉,就会消耗越多的电能。

OPENGL的虚拟管线须要大量的显存带宽来支持。咱们来举个例子,一个一般的用例:每一个像素须要从 深度/模板缓存中读出深度值进行比较,而后将新值写回深度/模板缓存,同时将颜色值写上颜色缓存,按照常规的状况,这里算做传输了12byte(color = 4byte, d/s = 4byte, d/s读写,color只写,一共4+4+4=12byte)。不过,这是在假设没有重复绘制,没有颜色混合,没有多通道算法,没有多采样抗锯齿的状况下。若是加上这些花哨的操做,一个像素传输100bytes是一件轻轻松松的事情。由于最多有4bytes的数据须要用来显示一个像素,这是一个对带宽和电能的过分消耗。实际上,桌面GPU一般都会采用压缩技术来减小带宽消耗,但他仍旧是一个显著的消耗。

 

为了减小这个凶残的带宽需求,大多数移动GPU都使用了tiled-based渲染。在最基础的层面,这些GPU将帧缓存(framebuffer),包括深度缓存,多采样缓存等等,从主内存移到了一块超高速的on-chip存储器上(注:相似cache,十分昂贵高速的存储器,为不致使歧义,on-chip就再也不翻译了)。这样,因为存储器on-chip了,他就和计算发生的芯片无限接近,这样,计算芯片就能以远低于常规消耗的电能来读写存储器了。若是咱们能够放置一块超大的高速on-chip芯片,那么这篇文章就能够到此为止了... 可是不幸的是,那样会须要太多的硅片了。所以,最终on-chip存储器,或称tile缓存,在有些GPU中小到只能容纳16x16个像素。

这样就带来了一些新的挑战:如何在如此小的一块tile缓存中渲染出高分辨率的图像?解决方案就是将OPENGL的帧缓存切割成16x16的小块(这就是tile-based渲染的命名由来),而后一次就渲染一块。对于每一块tile: 将有用的几何体提交进去,当渲染完成时,将tile的数据拷贝回主内存,如同图表23-1所示。这样,带宽的消耗就只来自于写回主内存了,那是一个较小的消耗:没有d/s,没有重绘的像素,没有多采样缓存。同时,消耗极高的深度/模板测试和颜色混合彻底的在计算芯片上就完成了。

如今,咱们回到OPENGL API。这个没有根据tile-based架构来设计的渲染API。OPENGL API是典型的当即模式:他描述在当前状态下须要绘制的三角形,而不是提供一个装载了全部三角形和其状态的场景结构。所以,在tile-based架构上实现opengl,咱们须要在一帧内收集全部提交过的三角形,并在以后再一并使用它们。相对的,在早期的固定管线gpu上,这一切工做都是经过软件方式完成的。如今大量的可编程移动平台gpu设计了专门的硬件单元来处理这件事(注:好比powervr的tiler等)。对于每一个三角形,咱们使用gl_position的输出语义来决定哪一个tile可能会被这个三角形影响,从而将这个三角形保存进这个tile的区域数据结构。同时,每个三角形还须要将当时的渲染状态打包,例如:ps的shader,常量寄存器,深度判断方式,等等。当一个tile渲染结束后,区域数据结构就会被用于查找和这个tile相关的三角形以及像素渲染状态。

这样看来,咱们貌似将一个带宽的问题挪到了另外一个地方:不一样于顶点数据当即的被光栅化而后被像素着色,在tile-based架构中,三角形会被保存下来以备以后使用。这样的话,就须要有足够的内存来保存原始顶点数据,顶点着色器的输出结果,三角形索引,像素状态,以及其余的一些在区域数据结构中的内容。咱们能够把这些收集的数据称为frame data(ARM文档把这些数据叫作多边形列表polygon lists,而Imagination Technologies文档把他们称为参数缓存parameter buffer)。tiled-based GPU是成功的,由于这些额外的数据读写对带宽需求通常会比咱们经过on-chip操做节省下来的带宽开销要小得多。只要提交的三角形保持在一个合适的水平,这个说法就一直是成立的。而过分的细分模型的几何表面,会使得frame data过分膨胀,从而致使tile-based GPU的优点再也不,反而因为frame data的高带宽消耗,反而比当即模式更慢。

 

上面的图表表示了tile-based GPU中数据的流向。最大的带宽消耗在像素处理器和tile buffer之间,他们都位于on-chip存储器上。相对的,下面的图表是当即模式GPU的数据流向,多采样,颜色,深度,模板缓存都位于主存储器上。数据的流向须要通过存储总线。

 

清空和抛弃FRAMEBUFFER   

当咱们在作性能调校时,关于tile-based GPU最须要铭记的一件事就是:咱们正在渲染的这一帧并非framebuffer而是frame data。他是生成framebuffer的一系列数据:转换过的顶点,多边形,状态切换。不一样于framebuffer,这些数据是随着DRAW CALL的增加而增加的。因此保证每一帧正确的终止是很是重要的,不然frame data会变得无穷的大。

当交换双缓冲窗口时,窗口系统会负责交换BACK BUFFER。在EGL和GLX这两种窗口系统中都有容许使BACK BUFFER失效的实现。所以,驱动程序能够在每次交换的时候,将frame data丢弃掉。而后从一张空白的画布开始。(EGL实现中,程序能够设置让后备缓冲保留,在下一节中会详细介绍)。

在使用framebuffer objects的时候,状况就变得更加复杂了,framebuffer objects不存在一个“交换”的操做。特别的,能够考虑使用glClear操做。典型的桌面GPU是当即模式架构的(immediate-mode),意味着他们在三角形准备好的时候就开始绘制像素。在一个当即模式的GPU上,一个glClear的调用就直接将clear值写入framebuffer了,所以这个代价是较高的。程序员们会使用各类技巧来避免这个操做,好比:若是他们知道接下来将会彻底覆盖的绘制,他们就不clear颜色buffer。交替的利用半深度空间来避免clear深度缓存。然而,这些技巧只是在之前有用,他们已经被硬件级别的优化给超过,甚至,可能因为和硬件优化冲突而下降效率!(注:这儿的说法实在不能彻底苟同...不少避免clear的操做仍是颇有用的)

在tile-based架构中,防止clear可能对于性能来讲是个灾难(注:没错,绝对是一个大灾难,致使你的帧率降低为1/4都有可能),由于每一帧都是构建在frame data中的,清空buffer的时候会简单的释放掉frame data中的已有的数据。换句话说,glClear的性能代价不只很是的低,并且他还能经过抛弃冗余的frame data而提升效率。

为了获得这个特性带来的所有好处,清空全部应该清空的framebuffer是颇有必要的。使用scissor,color write mask或者只clear一部分颜色/深度/模板缓存会阻碍frame data的清空(这里要特别注意,译者吃过这亏,在tilebased gpu上,scissor, stencil这些常见的性能优化手法在这里都是性能灾难…)。最安全和最易移植的方法如代码列表所示。

1 glDisable(GL_SCISSOR_TEST);
2 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
3 glDepthMask(GL_TRUE);
4 glStencilMask(0xFFFFFFFF);
5 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

这些操做须要在每一帧开始前完成,除非窗口系统已经代为操做。固然,mask和scissor若是已经设置正确后,就没必要每一次都显示的设置了。(通常不会普遍的使用这两个功能)(注:在PVR硬件上,若是不将COLORMASK归位,CLEAR直接就不会成功...)

上面的讨论都只局限于这个API上:glClear。glClear是一个底层命令,而不是一个高层的hint。若是你的程序须要同时运行在tile-based架构和immediate-mode架构上的话,就会比较尴尬。因此,OpenGL ES开发者须要可移植的性能的话,能够考虑EXT_discard_framebuffer这个扩展,他提供了这个hint。这个扩展的调用是glDiscardFramebufferEXT,他告诉驱动程序:当前bind的这个framebuffer已经没用了,你能够随时把他用来作任何事。tiled-based架构能够经过这个hint来更加显式的释放frame data,同时,immediate-mode架构能够忽略掉这个hint。代码列表23.2展现了应该如何使用这个Hint。

const GLenum attachments[3] = { COLOR_EXT , DEPTH_EXT , STENCIL_EXT };
glDiscardFramebufferEXT(GL_FRAMEBUFFER , 3, attachments);

Discards操做在framebuffer object,或者说render-to-texture上有另外的做用。当渲染一个3D几何体到一张纹理时,例如生成环境贴图,咱们须要使用深度缓存。可是,一旦渲染完后,咱们就不须要使用了。这时,咱们就能够经过调用glDiscardFramebufferEXT来告诉驱动程序能够释放frame data了。可是这个时候,咱们还不用unbind这个framebuffer object,同时tile-based GPU还可能根据这个hint,来决定不把depth从tile buffer拷贝回主内存。尽管还在桌面OPEN GL上还不可用,EXT_discard_framebuffer还对多采样抗锯齿的framebuffer起做用,多采样buffer能够在他解算回单采样buffer时就被抛弃掉,节约带宽。在写做这篇文章的这时,EXT_discard_framebuffer相对来讲还比较新,还须要一些实验来肯定这些Hints在各类特别的实现下到底有多高效。

 

增量式的帧更新                  

对于一个移动相机的3D场景,好比第一人称射击游戏,咱们有理由相信每个像素在每一帧都会改变,因此,每一帧清空framebuffer不会摧毁掉任何有用的信息。但对于更多GUI类的程序来讲,有不少相似控件或者消息窗口等不会改变的东西,他们没有必要每帧都从新生成。开发者在tile-based GPU上使用EGL时会常常惊奇的发现,backbuffer不会被保留到下一帧。EGL 1.4容许显式的经过EGL_SWAP_BEHAVIORL来申请保留,可是这在tile-basedGPU上不是默认设置,由于他会下降效率。

要理解为何back-buffer保留机制会下降效率,让咱们从新考虑一个tile-based GPU如何在一个tile内渲染像素。若是framebuffer在一帧开始时被clear掉,tile buffer只须要初始化全部像素为clear color便可。可是若是framebuffer须要保留,tile buffer就须要从原来的buffer中取出颜色,并安放到tile上正确的位置,这是须要大量带宽的。带宽的消耗能够看作是将上一帧图像做为纹理绘制到这一帧。是否使用帧保留,要根据场景的复杂度来决定,若是从新绘制一次这个帧都会比保留帧来得快,那么就选择每一帧都重绘,不然,选择帧保留。

高通提供了一个设备扩展(QCOM_tiled_rendering)来应对这种用例。程序能够显示的指明哪一个区域是准备更新的,而后全部不在这个区域的渲染都会被屏蔽掉。GPU只须要处理与这个区域有交集的tile,剩下的区域不会被触碰到,所以framebuffer会被保留下来。这个扩展一样包含了相似EXT_discard_framebuffer的特性,来容许用户显示的指出当前的内容是否须要保留。举个例子,咱们考虑一个程序,一个3D渲染的区域,在x,y处,长宽为w x h,将要被彻底的替换,剩下的区域都不须要改变。那么咱们就可使用以下的代码,来加速这个过程:

glStartTilingQCOM(x, y, width , height, GL_NONE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glViewport(x, y, width , height);
// Draw the scene
glEndTilingQCOM(GL_COLOR_BUFFER_BIT0_QCOM);
eglSwapBuffers(dpy, surface);

注意,咱们必需要为EGL的EGL_SWAP_BEHAVIOR设置为EGL_BUFFER_PRESERVED。

 

FLUSHING                    

Tiled-based GPU有时又被称做“延迟”的,这是由于显卡驱动程序会尽量的屏蔽掉没必要要的像素渲染。下面的这些操做,会致使framebuffer的内容被强制更新

  • eglSwapBuffers 以及和它相似的窗口系统操做
  • glFlush和glFinish
  • glReadPixels, glCopyTexImage, 和glBlitFramebuffer
  • 在当前帧使用遮挡查询
  • 使用render to texture的结果进行渲染
  • 切换framebuffer的绑定,例如glFramebufferRenderbuffer或者glRenderbufferStorage,又或者切换的RTT,都会致使framebuffer的flush。由于framedata只对原来的attachment有效。

在tiled-based gpu上,下面这个渲染方式的效率会十分低下:

  1. 绘制一些三角形
  2. 使用framebuffer的结果
  3. 绘制另一个三角形
  4. 使用framebuffer的结果
  5. 再绘制一个三角形...

当每次须要使用framebuffer结果的时候,都会致使另外一次全新的像素着色流程:最坏的状况是每个framebuffer中的像素都执行一次读取、写入的操做,即便你只须要绘制仅仅一个三角形。由于每一次像素着色流程的消耗都很是高,所以咱们的目标是尽可能每帧只存在一个像素着色流程。

即便是从GPU中取得framebuffer的内容,这个消耗也是存在的,好比说读取render to texture的纹理颜色或者调用glReadPixels去读取pixel pack buffer,由于每一次的“绘制-读取”操做都会须要像素着色流程再次运行。和桌面平台的当即模式GPU对比,glReadPixels在执行时的消耗基本上能够不用关心。

在某些driver下,glBindFramebuffer也会致使为绑定前的那个framebuffer开启一次像素着色流程。所以,一帧内,每一个framebuffer最好只绑定一次。举个例子,考虑一个场景,里面有一些光滑的物体,使用了实时生成的环境图。常规的作法是:在每一个物体渲染前,将这个物体须要的场景图渲染出来,而后使用。可是,这里更好的作法是,在绑定最终绘制的framebuffer前,就将全部物体须要的环境图所有生成好,这样可以减小framebuffer的重复绑定,从而提升效率。

除了上述的这些情景,这里还有一个状况会致使framebuffer的强制刷新。因为内存是有限的,因此用于framedata的内存大小会随着这一帧提交的几何体大小而变化,当你一直渲染愈来愈多的几何体而不进行framebuffer交换或者清空的话,程序最终会致使内存溢出。为了防止这个状况发生,driver最终会进行强制更新,来确保内存不会溢出。这个操做代价是十分大的,不一样于交换操做,全部的缓存,包括MSAA缓存,会被写到其余地方,以后再从新加载回来以继续渲染,这会致使一次16倍于常规强制刷新的贷款消耗。

这个状况,就意味着,场景的性能表现和渲染的三角面数量并非线性相关的。所以,咱们不能简单的用小场景来做性能测试,在估计当前程序可以承载的目标三角面数量时,有必要针对这些应用情景做一些检查。

 

渲染迟滞                        

因为一帧内的顶点和像素的处理是发生在相对独立的阶段的,应用程序会将CPU处理, 顶点处理,像素处理安排在相邻的三帧中。以下图所示。当一个渲染命令提交后,要在当帧以后的第三帧,渲染结果才会显示出来。

 

延迟除了会影响用户的操做感觉外,还会影响从GPU中往CPU回读信息的操做。同步的查询操做,例如glReadPixels,将会致使CPU挂起强制等待后两帧的返回。即便是异步查询,例如遮挡查询,查询结果最终会被读回,过于频繁的查询调用也会致使CPU挂起等待。

若是能够等待结果返回时再使用,那么 GL_QUERY_RESULT_AVAILABLE这个check就有用了。为当即模式编写的代码通常会判定,查询的结果在一个固定的时间段内必定会获得返回,或者能够等待带更长时间,甚至poll到它返回为止。一样的,若是必须使用glReadPixels的话,从那些已经完成的framebuffer上取得像素颜色而不是当前绘制的framebuffer会极大的提升效率,下降渲染迟滞,同时获得一个相对可接受的查询反馈。

迟滞还体如今另外一个重要的地方:当资源在使用时修改资源。一个广泛的例子就是动画mesh,这是一个每帧都须要更新顶点数据的用力。以前的顶点数据可能还在被上一帧的顶点计算单元使用,而这是若是应用程序要更新顶点缓存,那么这块内存必须等待上一帧的使用操做完成后才能被写入。在大多数状况下,drivers经过建立一块额外的内存拷贝来防止等待,(将新值写入拷贝内存,待以前的使用完成后再更新)。可是,在一个内存,贷款都十分吃紧的移动设备上,这个copy-on-write的发生是值得咱们关注的。特别的,当频繁更新的资源在一帧屡次使用时,这个问题会更加严重,会致使屡次的copy-on-write。因此,若是可能,全部的资源更新尽可能在资源使用前完成,咱们显式的进行copy-on-write的管理,而不是将它扔给driver。

在使用诸如EGL_KHR-image_pixmap或GLX_EXT_texture_from_pixmap这类扩展时须要很是当心,他们会修改操做系统的pixmaps。驱动程序一般不会有更多的自由在内存中移动这些资源。有可能会使得整个系统挂起,或是强制提交全部结果到framebuffer,而后从新加载。

tiled-based GPU一般比当即模式GPU有更高的迟滞,所以,为当即模式GPU优化的代码可能在tiled-based GPU上须要从新优化。

 

隐藏表面剔除                 

当即模式GPU处理重叠物体是用新像素覆盖已绘制像素,这里会有两个多余的消耗:一个是被覆盖像素的着色消耗,一个是被覆盖像素的冗余带宽消耗。在tiled-based gpu上,后一个消耗是不存在的,由于屏幕像素将在彻底处理结束以后再写会主内存,可是着色的消耗依然存在。因此,进行高层裁剪,从前日后的组织不透明物体的渲染仍然十分必要,这能够经过硬件的“预深度检测”(early depth test)来提升效率。由于上述这这些问题,在tiled-based gpu上,在CPU排序和GPU着色消耗之间的平衡方式的选择上可能和当即模式GPU有所不一样。

PowerVR的GPU家族,拥有像素着色阶段的逐像素表面剔除特性[Ima 11]。在运行任何像素shader以前,多边形会被预处理来决定哪些像素可能对最终的结果有贡献,以后,只执行这些有贡献的像素,剔除掉其余像素。这个剔除方式须要对几何体进行排序,要完成这个优化,像素shader的结果必需要确保可以彻底的覆盖他们遮挡的像素。而以下注入带有discard指令的shader,或者使用了Mask,alpha-test, alpha-to-coverage, 颜色混合特性将会屏蔽掉优化,由于他们“遮挡”的像素有可能对最终的图像产生贡献。所以,逐像素表面剔除特性只会对须要他们的物体开启。

当PowerVR系硬件的“隐藏面剔除”功能失效时,还有另一个选择,使用一个(深度流程)depth-only pass:关闭颜色写入,赋予空的像素shader来生成深度缓存,接下来再使用正确的像素shader来正式的绘制场景。depth-only pass会决定每一个可见像素的深度,接下来在真正绘制的时候,只有这些可见像素会被处理(经过预深度检测)。

depth-only-pass技术在当即模式GPU和tiled-basedGPU上对于复杂的着色计算场景来讲都是十分有效的优化手段,但trade-off倒是不一样的。在两种平台上,depth-only-pass都会增长顶点处理和光栅化的消耗。在当即模式GPU上,depth-only-pass会增长额外的带宽消耗,由于深度缓存的访问次数会翻倍。在tiled-based GPU上,depth-buffer的访问很快,不会增长主内存的带宽消耗,可是因为全部提交到frame data的几何体信息都复制了一次,所以这里会有一个较小一些的带宽消耗。所以,对于带宽瓶颈的程序来讲,depth-only-pass在当即模式GPU上没有优化效果时,换到tiled-based GPU上也许会有优化效果。

 

颜色混合                       

在当即模式GPU上,颜色混合一般是一个代价很高的操做,由于完成混合须要一个在framebuffer上的读取-写入循环,这个操做发生在相对较慢的主内存中。而在tiled-based GPU上,读取-写入循环彻底发生在芯片的快速存储器中,因此这个操做的代价很是小。一些GPU还专门实现了处理颜色混合的硬件,来使颜色混合变得几乎免费,其余的GPU通常都使用shader指令来实现颜色混合。所以,颜色混合会下降着色运算的最大指令数。

须要注意的是,咱们只是指出了颜色混合的直接消耗,让一个物体部分透明还带有间接消耗,由于这个物体不能被当作遮挡体了。被物体遮住的像素须要被处理,而若是不用透明,他们本该能够被隐藏面剔除或者预深度检测时被剔除。

 

多采样                         

多采样是一个相对高效的提高画面质量的技术,而又不用牺牲像超采样那样多的代价。每个在framebuffer上的像素都存储多个采样结果,这些采样结果将在最后生成抗锯齿图像。然而,被光栅化后的图源,每一个像素只用被着色一次。但这已经让像素着色消耗变得很大了,在immediate当即模式GPU上,多采样会带来很大的带宽消耗:4次多采样(一般的采样数选择),使得全部对framebuffer的读写操做的带宽消耗提升四倍。多种硬件在选择多采样位置的方法上进行优化,但多采样仍然是代价很高的操做。

相反的,在tile-based GPU上,多采样的代价是很小的,由于多采样的采样点只须要保留在on-chip缓存中,在全部处理完成后才写回framebuffer的主内存。所以,多采样不会带来多余的带宽消耗。

固然,这里依然会有两个消耗代价:

首先,4次多采样须要4倍的tile缓存。因为tile缓存容量至关的宝贵,一些GPU会在开启多采样时,缩小tile的尺寸,以容纳采样点须要的缓存。缩小的tile会带来一些额外的性能开销(每一个tile都须要存储更多的图元),可是减半tile的尺寸并不必定会减半性能,因此当程序瓶颈在像素着色时,只会看到一个很小的性能降低。

其次,多采样的另外一个开销(在immediate当即模式GPU上也会存在)则是在物体边缘会生成更多的像素。以下图所示,每一个多边形会检测到更多的像素。这还不止,这些多采样的区域,前景和背景几何体会同时向同一个像素贡献颜色以供混合,这些图元都须要被着色,所以硬件隐藏面剔除机制不能剔除掉这些图元。这些额外的图元消耗会根据场景的边缘多少而不一样,但10%是一个比较好的初始猜想值。

 

 

性能分析方法                 

在当即模式GPU上,ARB_timer_query扩展能够用来获取某一段场景渲染的消耗。以个范围内须要分析的渲染命令被glBeginQuery和glEndQuery包裹起来,而后这些渲染命令消耗的时间会被测量,并返回。

尽管这个扩展有可能被实如今tiled_based GPU上,但这个结果可能在帧粒度一下都不会有任何做用。这是由于,在tiled-based GPU上,命令不必定会按照他们提交的顺序来执行:全部的顶点处理会在第一遍完成,而后像素处理会按照tile的顺序依次执行。所以,性能分析须要依靠更多的干预技术,例如切换场景的启用/禁用开关,来观察性能的降低。硬件提供商提供的读取内部性能计数器的特供工具也能为肯定渲染流水线的性能瓶颈提供很大的帮助。

不一样于后期优化,在开始开发时就引入一个性能衡量基准,来帮系统决定三角形,纹理,着色器复杂度的预算是一个一般的好办法。在这样作的时候,必定要记住,当提交了过多的三角形而没有交换,会致使一个性能消耗的陡然提高,这在第五节中已经提过。同时,确保哪些渲染命令真正被执行了也是十分重要的,在渲染调用后放置一个glClear在他们到达GPU前取消这些渲染调用(由于他们根本不起做用)。

 

总结                              

每个GPU,每个设备驱动都是不一样的。而在优化和尝试中的不一样选择意味着惟一可以真正决定性能瓶颈的选择就是去实际的测试它。然而,下面的这些没有科学根据的规则,多是在tile-based GPU上发现高效率渲染的一个起点。

  • 在一帧的开始清空或抛弃整个渲染内容:包括颜色,深度,模板缓存。
  • 对于每个framebuffer object,一帧中只绑定一次,而且,在解绑或使用framebuffer object的结果以前,要确保全部影响它的渲染指令提交完毕。
  • 在使用遮挡查询或其余取得渲染结果的机制时,记得渲染迟滞效应。而且,若是该程序在以前为当即模式GPU优化过,那么,在tile-based GPU上须要从新优化。
  • 将三角面数量控制在一个合适的水平,尽可能避免大量的精细模型。
  • 在拥有内建的隐藏面提出的硬件上(POWER VR),就没有必要消耗CPU计算能力去从前日后的排序不透明物体了,在其余硬件上,这个步骤颇有必要。同时,能够考虑快速深度渲染(depth only pass)。
  • 利用代价较低的多采样。
  • 记住,在移动平台上,性能必须和电量消耗作一个平衡。
相关文章
相关标签/搜索