VR 与 AR 开发实战
内容简介
从虚渺的膨胀到实质的沉淀,本期 VR 与 AR 开发实战专题我们汇聚一线技术开发者,围绕性能优化、人机交互技术、新平台开发等话题,分享实践中心得与体验。
- HoloLens 开发与性能优化实践(张昌伟,微软(中国)有限公司 PFE 工程师)
- Unreal Engine 4 VR 应用的 CPU 性能优化和差异化(王文斓,英特尔软件与服务事业群的资深软件工程师)
- VR 中的交互之熵(王锐,开源图形引擎 OpenSceneGraph 的核心贡献者和推广者)
- ARKit:简单的增强现实(张嘉夫,专注于 Apple 平台的个人开发者)
本书内容
Web 端 VR 开发初探
文/张乾
随着硬件和软件技术的发展,产业界对虚拟现实(Virtual Reality)用户体验产生了重大期望。技术的进步也使我们可能通过现代浏览器借助开放 Web 平台获得这种用户体验。这将帮助 Web 成为创建、分发以及帮助用户获得虚拟现实应用和服务生态系统的重要基础平台。
引言
2016年最令科技界激动的话题,莫过于 VR 会如何改变世界。一些电影已开始涉足 VR,让用户不仅能看到 3D 影像,更能以“移形换影”之术身临其境,带来前所未有的沉浸式观影体验;此外,游戏领域也开始 VR 化,用户再也不用忍受游戏包里单一的场景。这些酷炫效果带来了巨大想象空间,VR 正在走近人们的生活。然而现实是,除了偶尔体验下黑科技的奇妙外,VR 并没有真正普及,在资本和硬件厂商*的背后,质疑声也此起彼伏。
目前,虽然 VR 硬件的发展已经走上了快车道,但内容却非常单薄。一部 VR 电影的成本相当高昂,VR 游戏也不逊色。内容创作成本的居高不下,导致了 VR 的曲高和寡。要想脱下那一层高冷的贵族华裳,飞入寻常百姓家,VR 尚需解决内容供给这一难题。以 HTML5 为代表的 Web 技术的发展,或将改变这一僵局。目前,最新的 Google Chrome 和 Mozilla Firefox 浏览器已经加入面向 HTML5 技术的 WebVR 功能支持,同时各方也正在起草并充实业界最新的 WebVR API 标准。基于 Web端的这些虚拟现实标准将进一步降低 VR 内容的技术创作成本及门槛,有利于世界上最大的开发者群体—HTML5(JavaScript)开发者进入 VR 内容创作领域。这不仅是 Web 技术发展历程上的显著突破,也为 VR 造就了借力腾飞的契机。
Web 端 VR 的优势
Web 可降低 VR 体验门槛
Web 技术不仅使创作 VR 的成本更加低廉,而且大大降低技术门槛。WebVR 依托于 WebGL 技术的高速发展,利用 GPU 执行计算以及游戏引擎技术针对芯片级的 API 优化,提高了图形渲染计算能力,大大降低开发者进入 VR 领域的门槛,同时 WebVR 还可以更好地结合云计算技术,补足 VR 终端的计算能力,加强交互体验。可以肯定,Web 扩展了 VR 的使用范围,广告营销,全景视频等领域已经涌现一批创新案例,很多生活化的内容也纳入了 VR 的创作之中,如实景旅游、新闻报道、虚拟购物等,其内容展示、交互都可以由 HTML5 引擎轻松创建出来。这无疑给其未来发展带来更多想象空间。
Web 开发者基数庞大
除了技术上的实现优势,Web 还能给 VR 带来一股巨大的创新动力,因为它拥有着广泛的应用范围与庞大的开发者基数,能帮助 VR 技术打赢一场人民战争,让 VR 不再只是产业大亨们的资本游戏,而是以平民化的姿态,进入广大用户日常生活的方方面面。
相信假以时日,VR 应用会像现在满目皆是的 App 一样,大量的 VR 开发者借助于 Web 端开发的低门槛而大量进入,同时各种稀奇古怪的创意层出不穷,虚拟现实成为电商商家必须的经营手段等。若到了这个阶段,VR 离真正的繁荣也就不远了。
开发 Web 端的 VR 内容
接下来我们通过实践操作来真正制作一些 Web 端的 VR 内容,体验 WebVR 的便捷优势。我们知道,许多 VR 体验是以应用程序的形式呈现的,这意味着你在体验 VR 前,必须进行搜索与下载。而 WebVR 则改变了这种形式,它将 VR 体验搬进了浏览器,Web+VR = WebVR 。在进入实践之前,下面先来分析一下 WebVR 实现的技术现状。
WebVR 开发的方式
在 Web 上开发 VR 应用,有下面三种方式:HTML5+ JavaScnipt + WebGL + WebVR API传统引擎 + Emscripten[1]第三方工具,如A-Frame[2]第一种方法是使用 WebGL 与 WebVR API 结合,在常规 Web 端三维应用的基础上通过 API 与 VR 设备进行交互,进而得到对应的 VR 实现。第二种是在传统引擎开发内容的基础上,比如 Unity、Unreal 等,使用 Emscripten 将 C/C++ 代码移植到 JavaScnipt版本中,进而实现 Web 端的 VR 。第三种是在封装第一种方法的基础上,专门面向没有编程基础的普通用户来生产 Web 端 VR 内容。在本文中我们主要以第一和第三种方法为例进行说明。
WebVR 草案
WebVR 是早期和实验性的 JavaScript API,它提供了访问如 Oculus Rift、HTC Vive 以及 Google Cardboard 等 VR 设备功能的 API。VR 应用需要高精度、低延迟的接口,才能传递一个可接受的体验。而对于类似 Device Orientation Event 接口,虽然能获取浅层的 VR 输入,但这并不能为高品质的 VR 提供必要的精度要求。WebVR 提供了专门访问 VR 硬件的接口,让开发者能构建舒适的 VR 体验。
WebVR API 目前可用于安装了 Firefox nightly 的 Oculus Rift、Chrome 的实验性版本和 Samsung Gear VR 的浏览器。
使用 A-Frame 开发 VR 内容
如果想以较低的门槛体验一把 WebVR 开发,那么可以使 MozVR 团队开发的 A-Frame 框架。A-Frame 是一个通过 HTML 创建 VR 体验的开源 WebVR 框架。通过该框架构建的 VR 场景能兼容智能手机、PC、 Oculus Rift和 HTC Vive。MozVR 团队开发 A-Frame 框架的的是:让构建 3D/VR 场景变得更易更快,以吸引 Web 开发社区进入 WebVR 的生态。WebVR 要成功,需要有内容。但目前只有很少一部分 WebGL 开发者,却有数以百万的 Web 开发者与设计师。A-Frame 要把 3D/VR 内容的创造权力赋予给每个人,其具有如下的优势与特点:
A-Frame 能减少冗余代码。冗余复杂的代码成为了尝鲜者的障碍,A-Frame 将复杂冗余的代码减至一行 HTML 代码,如创建场景则只需一个<a-scene>标签。
A-Frame 是专为 Web 开发者设计的。它基于 DOM,因此能像其他 Web 应用一样操作 3D/VR 内容。当然,也能结合 box、d3、React 等 JavaScript 框架一起使用。
A-Frame 让代码结构化。Three.js 代码通常是松散的,A-Frame 在 Three.js 之上构建了一个声明式的实体组件系统(entity-component-system)。另外,组件能发布并分享出去,其他开发者能以 HTML 的形式进行使用。
代码实现如下:
// 引入A-Frame框架<script src="./aframe.min.js"></script><a-scene> <!-- 定义并创建球体 --> <a-sphere position="0 1 -1" radius="1" color="#EF2D5E"></a-sphere> <!-- 定义交创建立方体 --> <a-box width="1" height="1" rotation="0 45 0" depth="1" color="#4CC3D9" position="-1 0.5 1"></a-box> <!-- 定义并创建圆柱体 --> <a-cylinder position="1 0.75 1" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder> <!-- 定义并创建底板 --> <a-plane rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane> <!-- 定义并创建基于颜色的天空盒背景--> <a-sky color="#ECECEC"></a-sky> <!-- 设置并指定摄像机的位置 --> <a-entity position="0 0 4"> <a-camera></a-camera> </a-entity></a-scene>
上述代码在 A-Frame 中执行的效果如图1所示。
图1 A-Frame 运行结果
使用 Three.js 开发 VR 内容
上文中我们提到另外了一种更加靠近底层同时更加灵活生产 WebVR 内容的方法,就是直接使用 WebGL+WebVR 的 API。这种方法相对于 A-Frame 的优势在于可以将VR 的支持方便地引入到我们自己的 Web3D 引擎中,同时对于底层,特别是渲染模块可以做更多优化操作从而提升 VR 运行时的性能与体验。
如果没有自己的 Web3D 引擎也没有关系,可以直接使用成熟的渲染框架,比如 Three.js 和 Babylon.js 等,这些都是比较流行且较为出色的 Web3D 端渲染引擎(框架)。接下来就以 Three.js 为例,说明如何在其上制作 WebVR 内容。
首先,对于任何渲染程序的三个要素是相似的,即是建立好 scene、renderer、camera。设置渲染器、场景以及摄像机的操作如下:
var renderer = new THREE.WebGLRenderer({antialias: true});renderer.setPixelRatio(window.devicePixelRatio); document.body.appendChild(renderer.domElement);// 创建Three.js的场景var scene = new THREE.Scene();// 创建Three.js的摄像机var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 10000);// 调用WebVR API中的摄像机控制器对象,并将其与主摄像机进行绑定var controls = new THREE.VRControls(camera);// 设置为站立姿态controls.standing = true;// 调用WebVR API中的渲染控制器对象,并将其与渲染器进行绑定var effect = new THREE.VREffect(renderer);effect.setSize(window.innerWidth, window.innerHeight);// 创建一个全局的VR管理器对象,并进行初始化的参数设置var params = { hideButton: false, // Default: false. isUndistorted: false // Default: false.};var manager = new WebVRManager(renderer, effect, params);
上述代码即完成了渲染前的初始化设置。接下来需要向场景中加具体的模型对象,主要操作如下所示:
function onTextureLoaded(texture) { texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; texture.repeat.set(boxSize, boxSize); var geometry = new THREE.BoxGeometry(boxSize, boxSize, boxSize); var material = new THREE.MeshBasicMaterial({ map: texture, color: 0x01BE00, side: THREE.BackSide }); // Align the skybox to the floor (which is at y=0). skybox = new THREE.Mesh(geometry, material); skybox.position.y = boxSize/2; scene.add(skybox); // For high end VR devices like Vive and Oculus, take into account the stage // parameters provided. setupStage();}// Create 3D objects.var geometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);var material = new THREE.MeshNormalMaterial();var targetMesh = new THREE.Mesh(geometry, material);var light = new THREE.DirectionalLight( 0xffffff, 1.5 );light.position.set( 10, 10, 10 ).normalize();scene.add( light ); var ambientLight = new THREE.AmbientLight(0xffffff);scene.add(ambientLight);var loader = new THREE.ObjectLoader();loader.load('./assets/scene.json', function (obj){ mesh = obj; // Add cube mesh to your three.js scene scene.add(mesh); mesh.traverse(function (node) { if (node instanceof THREE.Mesh) { node.geometry.computeVertexNormals(); } }); // Scale the object mesh.scale.x = 0.2; mesh.scale.y = 0.2; mesh.scale.z = 0.2; targetMesh = mesh; // Position target mesh to be right in front of you. targetMesh.position.set(0, controls.userHeight * 0.8, -1);});
最后的操作便是在 requestAnimationFrame 设置更新。在 animate 的函数中,我们要不断地获取 HMD 返回的信息以及对 camera 进行更新。
// Request animation frame loop functionvar lastRender = 0;function animate(timestamp) { var delta = Math.min(timestamp - lastRender, 500); lastRender = timestamp; // Update VR headset position and apply to camera. //更新获取HMD的信息 controls.update(); // Render the scene through the manager. //进行camera更新和场景绘制 manager.render(scene, camera, timestamp); requestAnimationFrame(animate);}
最后,程序运行的效果如图2所示,可以直接在手机上通过 VR 模式并配合 Google Cardboard 即可体验无需下载的 VR 内容[3]。
图2 Three.js 运行结果
上述示例中的代码实现可从[4]中下载。
经验与心得
通过上述介绍我们基本可以实现一个具有初步交互体验的 Web 端 VR 应用,但这只是第一步,单纯技术上的实现距离真正的可工程化还有一定差距。因为最终工程化之后面向用户的产品必须比技术原型要考虑更多具体的东西,比如渲染的质量、交互的流畅度、虚拟化的沉浸度等,这些都最终决定用户是否会持续使用产品、接受产品所提供的服务等,所以将上述技术在工程化应用之前还有很多的优化与改进工作要做。以下是个人在做 Web 端 VR 应用过程中体会的一些心得经验,分享出来供读者参考。
引擎的选用。如果是使用已有的 WebGL 引擎,则可参考[5]中的文档来进行 VR SDK 集成。这里边需要做到引擎层与 VR SDK 层兼容,以及 VR 模式与引擎的工具部分的整合,也可以参考桌面引擎如 Unity3D 和 Unreal 在 VR SDK 集成上的开发模式。如果选用第三方的 WebGL 引擎则有 Three.js 或 Babylon.js 等可选,这些主流的 WebGL 引擎都已经(部分功能)集成了 VR SDK。
调试的设备。调试 Web 端的 VR 应用同样需要有具体的 VR 设备的支持。对于桌面 WebM 内容还是要尽量使用 HTC Vive 或 Oculus 等强沉浸感 VR 设备。对于移动 Web 应用,由于 Android 平台上的各浏览器的差异较大,表现也会不太一致,所以建议使用 iOS 设备进行开发与调试,但是在最终发布前仍要对更多的 Andnoid 设备进行适配性测试与优化。
性能的优化。在 Web 端做三维的绘制与渲染,性能还是主要瓶颈,因而要尽可能的提高实时渲染的性能,这样才能有更多资源留给 VR 部分。目前的 WebVR 在渲染实时中并没有像桌面 VRSDK 一样可以调用众多的 GPU 底层接口做诸如 Stereo rendering 等深层次的优化,因而对性能的占用还是较多。
已知的问题。目前,WebVR 仍然不太稳定,还会有诸多的 Bug,比如某些情况下会有设备跟踪丢失的情况,而且效率也不是太高。大多数 WebVR 应用可以作为后期产品的储备和预研,但要推出真正可供用户使用并流畅体验的产品,还是有较长的路要走。
结束语
许多人将即将过去的2016称为 VR 元年,在这一年中 VR 的确经历了突飞猛进的发展,体现在技术与生态等各个方面。在新的2017年,相信 VR 必将会有更大的发展与进步,作为技术工作者,我们更应该从自身的技术专长作为出发点,参与到新技术对社会与生活的变革中来。
参考链接
[1] http://kripken.github.io/emscripten-site/
[2] https://aframe.io/
[3] http://www.shxt3d.com/ WebVR /index.html
[4] https://github.com/bugrunnerzhang/hello WebVR.git
[5] https://mozVR.com/
PC VR 游戏的 CPU 性能分析与优化
文/王文斓
伴随着全新 VR 体验所带来的双目渲染、高分辨率和低延时等要求,对 CPU 和 GPU 都造成了极大的计算压力,一旦 VR 应用出现性能问题,非常容易造成用户眩晕并带来极差的用户体验,因此性能问题对于 VR 体验的好坏格外重要。本文将集中介绍 VR 需要高计算量的原因,以及分享如何利用工具查找 VR 应用的性能问题和 CPU 瓶颈所在。
自从三大头显厂商 Oculus、HTC 和 Sony 在2016年发布了虚拟现实(VR)头显产品后,由于能够带来卓越的沉浸式体验,VR 越来越受到市场的关注和重视,而 VR 也被认为会取代智能手机成为下一代的计算平台。然而,尽管虚拟现实能给用户带来身临其境般的沉浸式体验,但相比传统应用,其具有双目渲染、低延迟、高分辨率以及高帧率等严苛要求,因此极大地增加了 CPU 和 GPU 的计算负载。鉴于此,性能问题对于虚拟现实应用尤为重要,因为 VR 体验如果没有经过优化,容易出现掉帧等问题,让用户使用时发生眩晕的情况。在本文中,我们将介绍一种适用于所有游戏引擎及虚拟现实运行时(VR runtime)的通用分析方法,分析基于 PC 的 VR 游戏面临的性能问题。我们以腾讯的一款 PC VR 游戏《猎影计划》为例展示如何利用这套方法进行分析。在此之前我们先来了解一下 VR 游戏对性能要求较传统游戏高的四大原因。
VR 游戏和传统游戏在硬件性能需求上的区别
相较于传统游戏,VR 游戏由于存在高帧率、双目渲染及容易产生眩晕等特性,导致对于硬件计算能力的需求显著上升。下面从四个方面比较一下 VR 游戏和传统游戏的区别:
像素填充率
以一个 1080p 60fps 游戏为例,像素填充率为 124M pixels/sec。如果是支持高端 VR 头盔(Oculus Rift、HTC Vive)的游戏,像素填充率为 233M pixels/sec(分辨率 2160x1200,帧率 90fps)。但是中间需要一个较大的渲染目标,避免图像经过反形变校正后产生用户可见并且没被渲染到的区域,导致视角(FOV)降低。根据 SteamVR 的建议,需要放大的比率为1.4倍,所以实际的像素填充率为 457M pixels/sec(分辨率 3024x1680,帧率90fps),我们可以通过 stencil mesh 把最终不会被用户看到的区域剔除掉以减少需要渲染的像素,经过优化后的像素填充率为 378M pixels/sec,但仍然是传统 1080p 60fps 游戏的3倍像素填充率。
图1 传统游戏渲染流程
双目渲染
从游戏渲染管线的角度来看,传统游戏中每一帧的渲染流程大致如下,其中蓝色的部分是 CPU 的工作,绿色的部分是 GPU 的工作。但由于视差的关系,VR 游戏需要对左右眼看到的画面分别渲染不同的图像,所以下面的渲染管线也要对左右眼各做一次,从而增加了计算需求(在 VR 中两眼的视差较小,可以利用 GBuffer 或提交渲染指令后用 view matrix 变换等方法降低实际计算)。
用户体验
对于传统游戏来说,平均帧率达标往往就代表了一个流畅的游戏体验。然而对于 VR 游戏来说,即使平均帧率达标,但只要出现了连续掉帧,哪怕只有非常少数的情况下才发生,都会破坏了整个游戏体验。这是由于连续掉帧会使用户产生眩晕,一旦产生眩晕的感觉,即使后续的画面不掉帧,用户已经感觉到不适,游戏体验已经打了折扣。所以在游戏设计的时候,需要确保场景在最差的情况下也能达标(高端头显下为 90fps),否则会影响游戏体验。另外,由于在 VR 场景中用户可以跟可移动区域内的对象作近距离观察和交互,所以必须开启抗锯齿以保证画面的清晰度。
延迟
在传统游戏里从控制输入到画面输出的延迟往往达到约 100ms 的等级[1],FPS 类别的游戏对延迟要求较高,但一般也在约 40ms 的等级。而 VR 里 MTP 延迟(motion-to-photon latency,从用户运动开始到相应画面显示到屏幕上所花的时间)低于 20ms 是基本要求,研究发现对于部分比较敏感的用户,延迟需要达到 15ms 甚至 7ms 以下[2]。
低延迟的要求除了使 VR 游戏必须运行在高帧率外,同时也降低了硬件的运行效率,导致同样的工作量需要更强的硬件来驱动,原因正是低延迟要求使 VR 游戏的渲染管线必须和传统游戏不一样,而其中 CPU 对 VR 性能的影响扮演了重要的角色。
VR 游戏和传统游戏在渲染管线上的区别
我们先来看看 VR 渲染管线和传统渲染管线的区别。如图2所示为传统游戏的渲染管线,其中 CPU 和 GPU 是并行处理的,以实现最高的硬件利用效率。但此方案并不适用于 VR,因为 VR 需要较低和稳定的渲染延迟,传统游戏的渲染管线无法满足此项要求。
图2 传统游戏的渲染管线
以图2为例,第 N+2 帧的渲染延迟会远高于 VR 对延迟的最低要求,因为 GPU 必须先完成第 N+1 帧的工作,再来处理第 N+2 帧的工作,因而使得第 N+2 帧产生了较高的延迟。此外,由于运行情况不同,我们可以发现第 N 帧、第 N+1 帧和第 N+2 帧的渲染延迟也会有所差异,这对 VR 的体验也是不利的,因为一直变动的延迟会让用户产生晕动症(simulation sickness)。
因此,VR 的渲染管线实际上如图3所示,这样能确保每帧可以达到最低的延迟。在图3中,CPU 和 GPU 的并行计算被打破了,这样虽然降低了效率,但可确保每帧实现较低和稳定的渲染延迟。在这种情况下,CPU 很容易成为 VR 的性能瓶颈,因为 GPU 必须等待 CPU 完成预渲染(绘制调用准备、动态阴影初始化、遮挡剔除等)才能开始工作。所以 CPU 优化有助于减少 GPU 的闲置时间,提高性能。
图3 VR 游戏的渲染管线
《猎影计划》VR 游戏背景
《猎影计划》是腾讯旗下利用 Unreal Engine 4 开发的一款基于 PC 的 DirectX 11 FPS 虚拟现实游戏,支持 Oculus Rift 和 HTC Vive。为了使《猎影计划》在英特尔处理器上实现最佳的游戏体验,我们与腾讯紧密合作,努力提升该游戏的性能与用户体验。测试结果显示,在本文所述的开发阶段,经优化后帧率得到了显着提升,从早期测试时跑在 Oculus Rift DK2(分辨率 1920x1080)上的每秒 36.4fps 提升至本次测试时跑在 HTC Vive(分辨率 2160x1200)上的每秒 71.4fps。以下为各阶段使用的引擎和 VR 运行时版本:
- 初始开发环境:Oculus v0.8 x64 运行时和 Unreal 4.10.2;
- 本次测试的开发环境:SteamVR v1463169981 和 Unreal 4.11.2。
之所以在开发阶段会使用到不同的 VR 运行时的原因在于,《猎影计划》最初是基于 Oculus Rift DK2 开发的,稍后才迁移至 HTC Vive。而测试显示采用不同的 VR 运行时在性能方面没有显着的差异,因为 SteamVR 和 Oculus 运行时采用了相同的 VR 渲染管线(如图3所示)。在此情况下,渲染性能主要由游戏引擎决定。这点可在图6和图15中得到验证,SteamVR 和 Oculus 运行时在每帧的 GPU 渲染结束后才插入 GPU 任务(用于镜头畸变校正),而且仅消耗了少量 GPU 时间(约 1ms)。
图4 优化前(左)后(右)的游戏截图
如图4所示为优化工作前后的游戏截图,优化之后绘制调用次数减少至原来的1/5,每帧的 GPU 执行时间平均从 15.1ms 缩短至 9.6ms,如图3和4所示:
测试平台的规格:
- 英特尔酷睿 i7-6820HK 处理器(4核,8线程)2.7GHz
- NVIDIA GeForce GTX980 16GB GDDR5
- 图形驱动程序版本:364.72
- 16GB DDR4 RAM
- Windows10 RTM Build 10586.164
初步分析性能问题
为了更好地了解《猎影计划》的性能瓶颈,我们先综合分析了该游戏的基本性能指标,详情见表1。表中数据通过几种不同的工具收集,包括 GPU -Z、TypePerf 和 Unreal Frontend 等。将这些数据与系统空闲时的数据比较可得出以下几点结论:
表1 优化前游戏的基本性能指标
- 游戏运行时的帧率低(36.4fps)而且 GPU 利用率也低(GTX980 上为49.64%)。如果能够提高 GPU 利用率,帧率也会提高。
- 大量的绘制调用。DirectX 11 中的渲染为单线程渲染,虽然微软提出 deferred rendering context[3]可以用另一线程对渲染指令进行缓存以实现多线程渲染,但结果差强人意[4]。所以相对于 DirectX 12,DirectX 11 渲染线程具有相对较高的绘制调用开销。由于该游戏是在 DirectX 11 上开发的,并且为了达到低延迟,VR 的渲染管线打破了 CPU 和 GPU 的并行计算,因此如果游戏的渲染线程工作较重,很容易会出现 CPU 瓶颈导致帧率显着降低。在这种情况下,较少的绘制调用有助于缓解渲染线程瓶颈。
- 由表中可以看出, CPU 利用率似乎不是问题,因为其平均值只有13.58%。但从下文更进一步的分析可以看出,《猎影计划》实际上存在 CPU 性能瓶颈,而平均 CPU 利用率高低并不能说明游戏是否存在 CPU 性能瓶颈。
下面我们会利用 GPU View 和 Windows 评估和部署工具包(Windows Assessment and Deployment Kit,Windows ADK)[5]中的 Windows 性能分析器(Windows Performance Analyzer,WPA)对《猎影计划》的性能瓶颈进行分析。
深入探查性能问题
GPU View[6]工具可用于调查图形应用、CPU 线程、图形驱动程序、Windows 图形内核等性能和相互之间的交互情况。该工具还可以在时间轴上显示程序是否存在 CPU 或 GPU 性能瓶颈。而 Windows 性能分析器可用于跟踪 Windows 事件(Event Tracing for Windows,ETW),并生成相应事件的数据和图表;WPA 同时具备灵活的用户界面(UI),通过简单操作即可查看调用堆栈、 CPU 热点、上下文切换热点等,它还可以用来定位引发性能问题的函数。 GPU View 和 Windows 性能分析器都可以用于分析由 Windows 性能记录器(Windows Performance Recorder,WPR)采集到的事件追踪日志(Event Trace Log,ETL)。Windows 性能记录器可通过用户界面或命令行运行,其内建的配置文件可用来选择要记录的事件。
对于 VR 应用,最好先确定其计算是否受限于 CPU、GPU 或二者皆是,以便将优化工作的重点集中在对性能影响最大的瓶颈,最大限度提升性能。
图5 GPUView 分析《猎影计划》时间线视图
图5为优化前《猎影计划》在 GPUView 中的时间线视图,其中包括 GPU 工作队列、CPU 上下文队列和 CPU 线程。根据图表我们可以看出:
- 帧率大约为 37fps。
- GPU 负载大约为50%。
- 此版本容易使用户眩晕,因为运行帧率远低于 90fps。
- 如 GPU 工作队列所示,只有两个进程向 GPU 提交了任务:Oculus VR 运行时和游戏本身。Oculus VR 运行时在帧渲染的最后阶段插入后处理工作,包括畸变校正、色彩校正和时间扭曲等。
- 从图中可以看出《猎影计划》同时存在 CPU 和 GPU 瓶颈。
- 在 CPU 瓶颈方面, GPU 有大约50%的时间都处于空闲状态,主要原因是受到了一些 CPU 线程的影响而导致 GPU 工作没法及时被提交,只有这些线程中的 CPU 任务完成后 GPU 任务才能被执行。这种情况下如果对 CPU 任务进行优化,将能够极大地提升 GPU 的利用率,使 GPU 能执行更多的任务,从而提高帧率。
- 在 GPU 瓶颈方面,从图中我们可以看出,即使所有 GPU 空闲时间都能够被消除,GPU 仍然需要大于 11.1ms 的时间才能完成一帧的渲染(这里约为 14.7ms),因此如果不对 GPU 进行优化,此游戏的帧率不可能达到 Oculus Rift CV1 和 HTC Vive 等 VR 头显要求的 90fps。
改善帧率的几点建议:
- 物理和 AI 等非紧急的 CPU 任务可以延后处理,使图形渲染工作能够尽早被提交,以缩短 CPU 瓶颈时间。
- 有效应用多线程技术可增加 CPU 并行性,减少游戏中的 CPU 瓶颈时间。
- 尽量减少或优化容易导致 CPU 瓶颈的渲染线程任务,如绘制调用、遮挡剔除等。
- 提前提交下一帧的 CPU 任务以提高 GPU 利用率。尽管 MTP 延迟会略有增加,但性能与效率会显着提高。
- DirectX 11 具有高绘制调用和驱动程序开销。绘制调用过多时渲染线程会造成严重的 CPU 瓶颈。如果可以的话考虑迁移至 DirectX 12。
- 优化 GPU 工作(如过度绘制、带宽、纹理填充率等),因为单帧的 GPU 处理时间大于 11.1ms,所以会发生丢帧。
为了更深入探查 CPU 的性能问题,我们结合 Windows 性能分析器来分析从 GPUView 中发现的 CPU 瓶颈(通过分析同一个 ETL 文件),以下介绍分析和优化的主要流程(Windows 性能分析器也可用于发现 CPU 上下文切换的性能热点,对该主题有兴趣的读者可以参考了解更多详情)。
图6 GPUView 分析《猎影计划》时间线视图(单帧)
首先我们需要在 GPUView 中定位出 VR 游戏存在性能问题的区间。在 GPU 完成一帧的渲染后,当前画面会通过显示桌面内容(Present)函数被提交到显示缓存,两个 Present 函数的执行所相隔的时间段为一帧的周期,如图6所示(26.78ms,相当于 37.34fps)。
注意导致 GPU 闲置的 CPU 线程。
注意在 GPU 工作队列中有不少时间 GPU 是闲置的(例如一开头的 7.37ms),这实际上是由 CPU 线程瓶颈所造成,即红框所圈起来的部分。原因在于绘制调用准备、遮挡剔除等 CPU 任务必须在 GPU 渲染命令提交之前完成。
如果使用 Windows 性能分析器分析 GPUView 所示的 CPU 瓶颈,我们就能找出导致 GPU 无法马上执行工作的对应 CPU 热点函数。图7-11显示 Windows 性能分析器在 GPUView 所示的同一区间下,各 CPU 线程的利用率和的调用堆栈。
图7 Windows 性能分析器分析《猎影计划》时间线视图,与图6为同一时间段
接下来让我们详细分析每个 CPU 线程的瓶颈。
由图8的调用堆栈可以看出,渲染线程中最主要的三个瓶颈是:
图8 渲染线程 T1864 的调用堆栈
- 静态网格的基本信道渲染(50%);
- 动态阴影初始化(17%);
- 计算视图可视性(17%)。
以上瓶颈是由于渲染线程中存在太多的绘制调用、状态变换和阴影图渲染所造成。优化渲染线程性能的几点建议如下:
- 在 Unity 中应用批处理或在 Unreal 中应用 actor 融合以减少静态网格绘制。将相近对象组合在一起,并使用细节层次(Level Of Detail,LOD)。合并材质以及将不同的纹理融入较大的纹理集都有助于提升性能。
- 在 Unity 中使用双宽渲染(Double Wide Rendering)或在 Unreal 中使用实例化立体渲染(Instanced Stereo Rendering),减少双目渲染的绘制调用提交开销。
- 减少或关闭实时阴影。因为接收动态阴影的对象将不会进行批处理,从而造成绘制调用问题。
- 减少使用会导致对象被多次渲染的特效(反射,逐像素光照,透明或多材质对象)。
图9显示游戏线程最主要的三个瓶颈是:
图9 游戏线程 T8292 的调用堆栈
- 设置动画评估并行处理的前置工作(36.4%);
- 重绘视口(view port)(21.2%);
- 处理鼠标移动事件(21.2%)。
以上三大问题可以通过减少视口数量,以及优化 CPU 并行动画评估的开销来解决,另外需要检查 CPU 方面的鼠标控制使用情况。
图10 工作线程 T8288 的调用堆栈
图11 工作线程 T4672 的调用堆栈
图12 工作线程 T8308 的调用堆栈
工作线程(T8288,T4672,T8308):这些工作线程的瓶颈主要集中在物理模拟,比如布料模拟、动画和粒子系统更新。表2列出了在 GPU 闲置(等待执行)时的 CPU 热点。
表2 优化前 GPU 闲置时的 CPU 热点
优化
在实施了包括细节层次、实体化立体渲染、动态阴影消除、延迟 CPU 任务以及优化物理等措施后,《猎影计划》的运行帧率从 Oculus Rift DK2(1920x1080)上的 36.4fps 提升至 HTC Vive (2160x1200)上的 71.4fps;同时由于 CPU 瓶颈减少,GPU 的利用率从54.7%提升至74.3%。
如图13和图14所示,分别为《猎影计划》优化前后的 GPU 利用率,如 GPU 工作队列所示。
图13 优化前《猎影计划》的 GPU 利用率
图14 优化后《猎影计划》的 GPU 利用率
图15所示为优化后《猎影计划》的 GPUView 视图。从图中可见优化后 CPU 瓶颈时间从 7.37ms 降至 2.62ms,所用的优化措施包括:
图15 优化后 GPUView分析《猎影计划》时间线视图
- 提前运行渲染线程(一种通过产生额外的 MTP 延迟来减少 CPU 瓶颈的方法);
- 优化绘制调用,包括采用细节层次、实体化立体渲染和移除动态阴影;
- 延迟处理逻辑线程和工作线程的任务。
图16 渲染线程 T10404 的调用堆栈
如图16所示为优化后 CPU 瓶颈期的渲染线程调用堆栈,即图15的红框标记起来的部分。
表3列出了优化后 GPU 闲置(等待执行)时的所有 CPU 热点,注意相对于表2,许多热点和线程已从 CPU 瓶颈中被移除。
表3 优化后 GPU 闲置时的 CPU 热点
更多的优化措施,比如 actor 融合或者精简材质,都可以优化渲染线程中的静态网络渲染,进一步提高帧率。假若能对 CPU 任务进行充分的优化,单帧的处理时间能进一步减少 2.62ms(单帧的 CPU 瓶颈时间),达到 87.8fps。
表4 优化前后游戏的基本性能指标
结论
利用多种工具分析 VR 应用可以帮助我们了解该应用的性能表现和瓶颈所在,这对于优化 VR 性能非常重要,因为单凭性能指标可能无法真正反映问题所在。本文讨论的方法与工具可用于分析使用任何游戏引擎及 VR 运行时开发的 PC VR 应用,确定应用是否存在 CPU 或 GPU 瓶颈。由于绘制调用准备、物理模拟、光照或阴影等因素的影响,有时候 CPU 对 VR 应用性能的影响比 GPU 更大。通过分析多个存在性能问题的 VR 游戏,我们发现其中许多都存在 CPU 瓶颈,这意味着优化 CPU 可以提升 GPU 利用率、性能及用户体验。
参考链接
[1]http://www.anandtech.com/show/2803
[2]http://blogs.valvesoftware.com/abrash/latency-the-sine-qua-non-of-ar-and-vr/
[3]https://msdn.microsoft.com/en-us/library/windows/desktop/ff476891(v=vs.85).aspx
[4]https://www.pcper.com/reviews/Editorial/What-Exactly-Draw-Call-and-What-Can-It-Do
[5]https://developer.microsoft.com/en-us/windows/hardware/windows-assessment-deployment-kit
[6]http://graphics.stanford.edu/~mdfisher/GPUView.html
VR 与 AR 开发实战——HoloLens 开发与性能优化实践
Unreal Engine 4 VR 应用的 CPU 性能优化和差异化
VR 中的交互之熵
ARKit:简单的增强现实
阅读全文: http://gitbook.cn/gitchat/geekbook/5a68274cd7affd0b5cf01e43
下一篇: 图片转Base64换行的问题