Sharing is caring.
—— Anonymous
说到虚拟拍摄,尤其相机内视效(In Camera VFX,下文简写为ICVFX)技术,延迟是绕不开的话题。于是每当业界出现了降低甚至声称解决延迟的方法,朋友圈都会像过节般地热闹一下。最近忙完了手头的事儿,挑了个慵懒的午后,品着新茶笃悠悠地整理下我所了解的虚拍延迟的种种原因以及改善思路。
我们常说ICVFX的延迟在7~8帧左右,那我们首先需要了解这7~8帧是在怎样的环境下用什么方式测量的?毕竟抛开剂量谈毒性是一种“耍流氓”的行为,你是要24 FPS下延迟1帧还是60 FPS下延迟2帧?
回到测量方法,一个简单可行的方法是首先将整个ICVFX的系统通过genlock锁在30 FPS下,然后使用慢镜头拍摄相机追踪设备由静到动并最终驱动LED上的内视锥的过程。之后通过计算慢镜头中追踪设备开始移动和LED内视锥发生移动的时间差,就能获得整个系统的输入延迟。实际操作中,我们往往会在画面中放个计时器,通过计算两个时刻中计时器的读数差来获得系统延迟。之前我常用30 FPS时码源发生器作为计时器,这导致测量误差在33毫秒左右。最近由于从同事那边薅了台240 Hz高刷的笔记本。于是我可以在笔记本上跑一个亚毫秒级高精度计时器,从而获得更高精度的测量值。
简单总结来说,我们说的7~8帧指30 FPS下整个ICVFX系统的输入延迟大约在233~267毫秒。这导致当你快速摇相机时,内视锥会有肉眼可见的滞后。那为什么我们在玩游戏的时候,很少感觉到类似的滞后呢?
Nvidia官方介绍Reflex SDK的这篇文章(https://www.nvidia.cn/geforce/news/reflex-low-latency-platform/)做了很好的说明。文中提到在使用普通的键鼠+显示器时,使用虚幻引擎(下文简写为UE)制作的《堡垒之夜》默认设置下输入延迟不足60毫秒。但注意这是在60 FPS的前提下,做个简单粗暴的换算,30 FPS下就是120毫秒不到。这仍然要比ICVFX系统快了一倍!那么大家同是UE的应用,ICVFX的时间都用在哪儿了?
让我们来对比下两种情况下的异同,上正文~
一系统延迟的组成
< 游戏的输入延迟 图片来自Nvidia的技术博客>
<ICVFX的输入延迟>
1. 外设延迟(Peripheral Latency)
设备延迟主要取决于设备的数据处理能力,一般处理能力越强,其poll rate(设备汇报其最新状态的频率)越高,鼠标可以做到轻轻松松上百。高端游戏鼠标可以做到1000 Hz,那么其理论延迟就只有1毫秒。而在ICVFX环境下,如果我们把相机追踪设备做genlock同步,跑在30 FPS下,那就会导致33毫秒的延迟。
至于传输延迟,游戏鼠标往往走USB,而相机追踪设备则是通过UE Live Link协议在局域网内进行通信,虽有快慢,但相较于其他部分可以忽略不计。
2. PC 延迟(PC latency)
采样(Sampling)延迟取决于游戏运行的频率,主流游戏60 Hz只能算过及格线,电竞游戏跑几百帧也不奇怪;ICVFX下由于影视场景一般比较重,再加上常常需要一个UE进程渲染内外两个视锥,所以一般锁在24~30 FPS,少数轻量级场景或者需要升格拍摄的情况会尝试锁60。
UE的延迟:UE的默认渲染管线会允许额外1帧的延迟,即游戏的游戏线程可以超前渲染线程1帧,这样做的好处是UE有更多的时间来进行World Tick以及场景渲染,副作用自然是额外造成的1帧延迟。
GPU的延迟:主要是对于瓶颈在GPU的应用场景,会有额外的Render Queue上的等待时间。然而对于游戏,特别是非固定帧率的游戏可以通过类似Nvidia Reflex技术,根据RenderQueue的实际负荷,智能延迟游戏线程上的采样时间点,从而避免在Render Queue阶段无谓的等待,从而改善延迟。但遗憾的是对于ICVFX,研发组在早期的测试时发现在Mosaic+SLI的情况下,Reflex并不能正确工作,这点也许将来可以通过和Nvidia技术团队的合作能得以解决。
至于Composite延迟,只要开启全屏模式,就都会跳过这一阶段。
通过UE的Reflex 插件提供的计算Latency的API可以很方便地获得PC Latency。在30 FPS下,我的ICVFX测试场景的PC Latency大概在85毫秒左右,也就是在2~3帧之间。
3. 显示延迟(Display Latency)
显示延迟其实是造成ICVFX额外延迟的主要原因。它本身有专业仪器可以测量,可惜我并没有,所以只能反过来推算整个显示阶段的延迟应该在 8(总延迟)- 1(外设延迟)- 2~3(PC延迟)= 4~5帧。
而一般游戏用的PC 显示器能做到1帧(全屏且开启VSync)以内,而ICVFX显示的额外3~4帧延迟主要来自LED处理器的各种处理耗时(拼接、后处理等)和额外多出的一组发送+采集的耗时。
二 改善的思路
既然知道了延迟产生的原因,那我们就可以分而治之,让ICVFX的延迟尽可能接近游戏的输入延迟。
1. 设备延迟
将Camera Tracking设备跑在最高频率上。主流的Camera Tracking方案最高能做到180~200 Hz的数据发送频率,这样算来理想状况下,我们的延迟能从33毫秒降到5毫秒。
2. PC延迟
优化工程,使其上屏帧率从24~30 FPS提升到60 FPS。这可以降低整个PC阶段的处理延迟。
不允许UE的游戏线程超前渲染线程一帧,当帧事当帧毕。这听上去挺复杂,其实就是一句CVar而已 —— r.oneframethreadlag 0(默认为1)。但这对于复杂场景(比如游戏与渲染线程负载都高)并不适用,因为不允许额外的这一帧会造成来不及Tick或者渲染,从而无法锁在目标帧率上。但好消息是对用于ICVFX的UE场景来说,游戏线程往往负载并不高,所以很多时候都能适用。有兴趣的同学可以顺便再好好看看官方的这篇文档(https://docs.unrealengine.com/5.0/zh-CN/low-latency-frame-syncing-in-unreal-engine/),通过文中介绍的方法平衡性能与延迟。
我猜这时你心里会想大家都是成年人,做什么选择题?我既要跑重场景,又要降延迟可不可以?目前看来有一种思路是很有希望的,但这需要一定“魔改”引擎的能力。思路的灵感来自于VR,开发过XR应用的同学们都知道对于XR的应用延迟也是衡量XR方案好坏的核心指标之一。而引擎为了降低XR HMD以及手柄的延迟,提出了一项叫做 “Late Update”的技术。对于由Live Link驱动的相机姿态,我们不使用上一帧游戏线程传到渲染线程中的数据,而是在渲染线程使用相机姿态绘制之前,去查询并使用相机Live Link收到的最新姿态数据,从而降低延迟!为了验证这一想法,我快速hack了个实现,并在本地做了个单机测试。在genlock 30 FPS + HTC Vive Mars作为追踪方案的情况下,延迟降低了近30毫秒。当然我测试的环境是单机,实际生产环境是多机同步,所以还会牵涉到如何保证每台机器拿到的最新的Live Link数据一致性的问题。
<通过Late Update,从 Render 3开始使用的相机姿态不再是相应Game帧中传递的,而是最新Game帧中的状态,从而降低一帧延迟>
给HTC VIVE Mars打个Call,方便快捷,应有尽有
至于Reflex,其实和Late Update的思路有些相似。Reflex延迟了游戏线程中的采样点,从而能采到更新的数据。如果你的LED Stage没有用Mosaic或者SLI,可以尝试开启,哪怕Reflex不管用,其附带的Boost模式相当于给GPU做了一键超频,谁不想要快点的GPU呢?只不过鉴于ICVFX的工作站都是劳模,长期996,忙起来007,所以务必注意散热,还有GPU电源线的质量(别问我是怎么知道的)。
3. 显示延迟
LED处理器厂商在降低延迟上也可以说是八仙过海了。通过关闭不必要的后处理,增加IO并行性或者暴力堆硬件的方式来改善这一问题。通过开启低延迟模式,一般都能降低1~2帧的延迟。
另外随着UE对SMPTE2110&Rivermax支持的不断完善,也有望降低显示延迟。
以上是所有的正文,最后按照惯例感慨下:就如同人无法避免死亡和税收一样,ICVFX的延迟同样无法避免。Epic Games从来不回避包括延迟在内的ICVFX所面临的限制,而是会与所有业界伙伴紧密合作,共克难题,并将我们最新的解决方案无私分享给每一位创作者。
























