移动VR设备中通过采用单缓冲器条带式渲染来降低延迟

作者:Christian Pötzsch

在移动手机端VR应用需要更多的组件支持。首先是传感器能够跟踪记录用户头部的运动,CPU驱动VR应用程序(大多都是在后台运行),GPU负责处理和计算来生成正确的显示图像,向用户或观察者呈现变换后的内容。

所有这些组件需要密切协作才能给用户提供逼真的使用体验。众所周知都会有一定延迟时间,这段时间被命名为“motion to photon latency”(即从用户运动开始到相应画面显示到屏幕上所花的时间)。尽管它是一个专业术语,但是却很好的描述了问题:伴随用户头部的运动,VR设备何时能够意识到并给用户展示正确的场景。

在真实世界中,这种情形很普遍以至于我们都没有意识到——否则我们就会经常走路撞墙了。然而将一个手机挂在头部前端显示VR场景,想要达到同样的效果是非常困难的。原因有很多种:计算机运行的时钟频率是固定的,流水线采用串行处理数据,最明显的就是最终图像渲染花费的时间。

我们作为一个GPU IP研发公司,当然非常有兴趣优化我们的显卡处理流水线以降低延迟,减少PowerVR显卡处理器渲染最终显示图像所耗费的时间。

对于Android系统和设备的组件要求

最终在安卓屏幕上显示的内容要采用组件的形式完成渲染。通常会有几个厂家自定义符合自己要求的图像,它们有自己的系统UI(显示状态栏和导航栏等)和前台应用程序(在设置的缓冲区中完成图像的渲染)。

这些缓冲区会被一个“消费者”线程所占用并且会直接反应在屏幕的显示效果上,大多数情况下在一个系统级应用中这个“消费者”线程通常被称为SurfaceFlinger服务。

如果硬件支持SurfaceFlinger就可以决定将这些缓冲区中渲染的图像直接在屏幕上显示(以正确的组织布局)。这个模式被称为硬件组件而且需要硬件显示设备的直接支持。如果硬件显示上不支持这个组织布局,SurfaceFlinger服务会采用一个帧缓冲器,利用GPU完成所有图像的渲染并保存在帧缓冲器中,最后像正常情况一样在显示器上完成图像组件的展示。

1

展示组件的处理过程

生成者线程和消费者线程都是相互高度独立的——实际上他们是不同的进程,采用跨进程的方式实现数据通信。现在如果一方出现错误,就会出现混乱和意想不到的问题。

举个例子生产者对一个正在向用户显示的帧缓冲区进行渲染操作,那么用户就会产生视觉冲突,这种情况称为“撕裂”效应。屏幕上的图像一半是过时的内容,而另一部分是重新经过渲染的图像。“撕裂”效应很容易判别,因为屏幕上会有明显的切割线。


一个简单的动画展示“撕裂”效应

为了防止“撕裂”效应,有两个关键因素是必须的。首先就是各部分之间要保证适当的同步,其次就是采用双缓冲技术。

同步操作可以采用安卓“Native Syncs”框架来实现,这个框架的一些标准和规范让它能够应用于安卓系统,它们借助内核空间来实现系统全局同步,在用户模式下使用文件描述符可以实现不同进程间的数据共享。

它们也是非复用的二进制同步机制,仅有“无信号”和“有信号”两个状态,并且只有一种状态的转换就是从“无信号”到“有信号”(状态是不可逆的)。

双缓冲技术可以实现在旧的图像内容正在被消费者线程所占用的同时,生产者可以渲染新的图像内容。当生产者完成渲染操作,两个缓冲区就会被转换,消费者则调用最新渲染的图像内容,与此同时生产者则开始渲染刚刚还在被消费者线程调用的图像内容。

上面这两种机制都是必须的,这样才能在安卓系统上实现流畅的图像输出,但是不幸的是这仍然还有额外的延迟花销。

就在三月份Khronos Group组织发布了“KHR可变的渲染缓冲器EGL扩展”规范,我们利用这个规范实现了上述单缓冲器渲染功能。

这个扩展标准需要GPU驱动和安卓操作系统的支持,正如我之前解释的那样,安卓系统很长时间一直都组织这种模式的操作,因为这样会产生图像“撕裂”效应。

那么当我们使用新的单缓冲器技术模式时如何组织“撕裂”效应的发生呢?

屏幕技术

回答这个问题之前我们需要了解一下显示器是如何工作的。GPU驱动会向显示器驱动推送一个帧缓冲数据包,采用的数据格式是显示器能够解析的。这就可能对帧缓冲数据格式有不同的要求,例如特殊的形变或者纹理对齐。

显示器会在下一画面显示新的帧缓冲图像,假设显示器从顶部到底部刷新新的图像,“beam(刷新分割线)”又从显示器屏幕底部返回到顶部,“vsync”或者垂直同步能够及时的实现就比较关键了。

很明显现在“beam”已经不再使用了,但是这种命名法仍然被采用。因为现在显示器不会再从帧缓冲器中取出任何数据,我们完全可以直接切换新的图像,并且确保不会出现“撕裂”现象。显示器完成这个操作花费的时间是固定的,与显示器的时钟周期相关。

通常一般的显示器刷新周期是16.7ms,这意味着屏幕上的画面每秒钟可以刷新60次。

3

显示器扫描加载过程

现在的手机通常采用16:9的显示比例,屏幕都是安装好的因此对扫描加载过程进行了优化,采用竖屏的加载模式,因为大多数用户每天都是这样使用他们的手机的。
对于VR则不同,VR应用则是横向显示,因此显示器加载方向变成从左到右进行。
因此让我们看看如何更新帧缓冲器同时又要在屏幕上显示,这是相当重要的。

条带式渲染

当显示器扫描加载缓冲器中的图像时,它的扫描加载速率通常是固定的。条带式渲染技术则是改变缓冲器中未被显示器扫描加载的那部分图像数据。主要分为两种不同的策略,在显示器下一次扫描加载过程中改变屏幕显示的部分画面。

这个被称为光线追踪,我们需要在扫描加载线的前面进行渲染然后更新帧缓冲器中的内容,这样在显示器屏幕上的内容就会不同。另一种策略则是光线追赶,意思就是滞后扫描加载线,更新其后面的内容,正好与上一种策略相反。

光线追踪技术能更好的解决延迟问题,但是同时也更难实现。GPU需要保证在非常紧张的实现内完成图像渲染操作。这种保证在多进程的操作系统中很难完成,因为大部分应用进程都在后台运行,与此同时GPU还要完成另一个帧缓冲器的渲染任务。因此更为简单的实现方法则是光线追赶策略。

4

光线追赶策略实现条带式渲染

VR应用需要向显示器请求最新一帧画面的扫描加载时间,并实时调整自己以确保完成全屏幕的渲染任务,然后再屏幕上呈现。为了计算一个条带开始渲染的时刻,首先我们需要定义条带的尺寸。条带的尺寸和条带的数量是可以定义的,这取决于显示器扫描加载的速度以及我们渲染每个条带的所需要的时间。在大多数情况下一般采用两个条带式最优的。在VR应用中正好分为两部分对应人的左右眼(我们是从左向右进行扫描加载),这样实现起来就更加容易了。在下面的例子中我们使用四个条带来向大家展示是如何工作的,前面提到过渲染整幅画面的时间是16.7ms。

通常渲染每个条带的时间可以是4.17ms。VR应用在显示完最后一帧画面后等待4.17ms然后开始渲染第一个条带,渲染第二个条带时则需要再等待4.17ms(则从开始一共等待了8.34ms),重复这样的操作直到四个条带都完成渲染,然后开始新一轮的操作。显然我们需要考虑每帧渲染提交所用的时间,并适当调整我们的时序,使用绝对时间证明是最准确的方法。


渲染VR模拟图像使用的条带分离技术

最后的一点儿想法

降低移动设备中VR应用的延迟是现在开发者面临的重大挑战之一。这不同于桌面的VR解决方案,桌面解决方案拥有更好的处理能力而且也不会遇到散热问题的限制。

原文链接:
http://blog.imgtec.com/powervr/reducing-latency-in-vr-by-using-single-bu...

声明:
本文为原创文章,转载需注明作者、出处及原文链接,否则,本网站将保留追究其法律责任的权利

--电子创新网--
粤ICP备12070055号