首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

基于区域的flush

基于区域的flush

基于区域的flush在mobile GPU中已经变得越来越重要了,这种技术能极大地缩短GPU运行当前这个flush的时间,从而带来性能上的巨大改善。然而很多用户对这个技术,或者换个词说,这种优化,仍然不是非常了解,有时候会和一些OpenGL ES的概念viewport或者scissor搞混淆掉。写这篇blog的目的就是阐述这种技术,以及OpenGL ES有哪些API可以应用这种技术。
原理GPU为了满足一些API的需要会flush当前的framebuffer。当eglSwapbuffers调用的时候,整个framebuffer都会被flush,然后绘制到屏幕上。但是往往还有其他情况,用户可能需要flush framebuffer,但是并不需要整个framebuffer的内容,而是仅仅一部分framebuffer的内容,比如说当glReadPixels被调用的时候,如果用户只希望读取某一个点的数据,那么最理想的情况就是只把这个点给flush出来然后传给用户,而不是flush全屏的framebuffer。如果framebuffer大小是1920x1080,flush仅仅一个点和flush 1920x1080个点会是成千上万倍的差距。
基于区域的flush就是这种技术,用最小的代价来flush用户规定的区域,从而提高性能。区域外的像素在flush过程中会保持不变,这是非常重要的。到底是flush整个屏幕还是flush某一块区域,这是一个软件上的选择。
glViewport和glScissor很多人会把基于区域的flush和glViewport以及glScissor搞混淆,是不是OpenGL ES规定了这种技术呢?首先在大多数GPU中viewport和scissor都是硬件设置,这个和之前我谈到的基于区域的flush是一种软件选择已经有区别了。
让我们先来看一下glViewport。glViewport规定的是当前绘制视窗的大小,它代表的是全屏的概念,所以很容易区分出来它和基于区域的flush是完全不一样的。
让我们再来看一下glScissor。scissor box和这里所说的区域的概念非常类似,唯一的区别是scissor是可以通过glEnable和glDisable来控制的。从我的个人角度出发,GPU的确可以为scissor专门设计这个优化,在每一个drawcall下来的时候根据scissor box来选择要flush的内容。但是如果你读了我的同事Peter Harris的的话,你会了解Mali GPU对每一渲染目标使用独特的两步渲染算法,首先执行所有的几何处理,然后执行所有的片段处理。这就决定了基于区块渲染的GPU不适合针对每一个drawcall来做基于区域的flush。
glCopyTexSubImage2D和glReadPixelsOpenGL ES中的这两个API函数就比较适合基于区域的flush这个技术。
在上面我们已经阐述了glReadPixels这个函数,用户仅仅需要区域内的像素内容,这个区域是由glReadPixels这个函数规定的。所以如果你选择把整个framebuffer的内容都给flush,这会是一个很大的浪费。
glCopyTexSubImage2D也很相似。这个API函数会把framebuffer的一部分拷贝到指定的texture中,从而达到替换texture部分区域的一个目的。这也和这个技术非常吻合,我们可以选择flush需要拷贝的那部分framebuffer的内容来满足用户的需求。
EGL_KHR_partial_update不久前Khronos发布了一个新的extension - 。这个extension规定用户在开始绘制前,可以通过eglSetDamageRegionKHR这个API函数来设置几个区域,接下来所有的OpenGL ES的绘制都限制在这几个区域之内,最后在eglSwapbuffers调用的时候,仅仅这几个区域的像素需要被flush并且显示,区域外的像素保持上一次绘制的结果不变。
这个extension从一个spec的角度阐释了什么叫基于区域的flush,以及如果用户想选择这种技术的话,应该选用什么API函数来实现绘制。
基于区域的flush和全屏flush的一些思考基于区域的flush是不是一定比全屏flush的性能要好?很多人看了上面的阐述觉得答案应该是肯定的。假设我们在glReadPixels里面用了基于区域的flush这种技术,让我们思考下面这种情况,
glDraw...(...);int val[16x16];int x = 0, y = 0;for (int i = 0; i < 1000; i++){   glReadPixels(x++, y++, 16,16, GL_RGBA, GL_UNSIGNED_BYTE, val); // 假设x, y是一个有效的值}
假设当前framebuffer大小是128x128,并且没有multisample。
如果是基于全屏的flush,我们只需要flush一次,然后每次调用glReadPixels的时候用已经flush的framebuffer里面读取需要的值就可以了。让我们计算下总的flush的像素个数是128x128 = 16384。
如果采取基于区域的flush,因为每一次flush的区域都不一样,所以这里就需要调用1000次flush。让我们计算下总的flush的像素个数是16x16x1000 = 256000。
看到这里,你是不是还觉得基于区域的flush的性能一定会比全屏flush的性能要好呢?
总结从上面的阐述中我们可以看到基于区域的flush是一种非常好的优化,但是并不是万能的。这种优化很大程度上依赖于厂商自己的实现和硬件的性能。如果这个区域的大小和全屏大小相差不大,或者像上面那种情况所描述的多次flush的情况,采取基于区域的flush这种技术是得不偿失的。
返回列表