Cesium在渲染一个新的帧时默认与游戏引擎的做法一致:通常与目标帧具有相同的速率。这在具备动态数据的Cesium应用或是具备持续数据流的视图时运行良好,许多Cesium应用在较低渲染频率时会从中受益。在渲染一个新的帧时会占用CPU资源,这通常在应用空闲的时候是没有必要的。通过显式渲染来提高性能意味着你在运行Cesium应用时不必担心笔记本电脑风扇疯狂运转或对移动设备的电池产生损耗。
从Cesium 1.42开始,一个可选的显式渲染模式允许开发者在Cesium中根据应用需要准确控制渲染。在可用状态下,只在以下场合对新的帧进行渲染:
- 相机切换,无论是用户操作还是通过Cesium API控制的;
- 模拟时间超过了指定的阈值;
- 在加载地形、图像、3D tiles或数据源时,包括个人瓦片的加载。在低等级时,当网络请求通过URI或BLOB解析、或web worker返回异步过程时会触发;
Globe对象图像层( imagery layers)添加、移除或发生变化; - Globe对象地形数据源( terrain providers)改变;
- Sence对象的模式发生改变;
- 通过Cesium API对一个帧进行显式渲染。
这包括了Cesium通常使用的很多功能,比如加载地形、图像和像3D Tiles的数据。多了解一下场景细节上的变化,而不是观察每个可能引起渲染的更新,这是由应用决定的。应用使得场景改变或者通过Cesium API得到的内容不属于之前提到的情形,比如使用了Entity或时Primitive API,那么需要显式地请求一个新的渲染帧。
当显式渲染开启并且场景变化时,渲染就会像平时一样与目标帧同一速率出现。无论如何,当Cesium空闲时,CPU的使用率会大大减少。举个例子,使用Chrome开发者工具进行测试,Cesium场景空闲时CPU的使用率平均是25.1%,开启性能提升后,CPU使用率平均是3.0%。这是在i7处理器的笔记本电脑上,用Google的Chrome进行测试的。
时间线展示了一般渲染和请求渲染模式情况下FPS和CPU使用随时间变化的情况。在初始加载时,两者接近。在初始加载后,请求渲染模式下,新的帧没有被渲染。这使得CPU的使用率几乎为0%。场景初始加载时FPS像期望的一样是60,但是在空闲时因为没有新的帧请求所以FPS讲到了0。
配置Cesium应用使用显式渲染,考虑到模拟时间要求渲染模式开启,之后所有不自动处理的案例都必须显式渲染帧。
开启请求的渲染模式
开启 requestRenderMode 以减少Cesium渲染一个新帧总的时间,并且降低应用中Cesium的CPU使用率。
var viewer = new Cesium.Viewer('cesiumContainer', {
requestRenderMode : true
});
可以同时在视图创建后调整以适应场景。
var viewer = new Cesium.Viewer('cesiumContainer');
viewer.scene.requestRenderMode = true;
处理模拟时间改变
当 requestRenderMode开启时,模拟时间默认变化超过0.0秒时,一个新的帧会被请求。这个时间值可以通过设置 maximumRenderTimeChange进行调整。如果你的场景有元素根据时间改变,比如动画、光线变化、水体掩膜或是视频,可以考虑进行相应设置。如果你的场景中没有根据模拟时间变化的元素,可以考虑把 maximumRenderTimeChange设置为一个较高的值,比如无限。
// Create a viewer that will not render frames based on changes in // simulation time.
var viewer = new Cesium.Viewer('cesiumContainer', {
requestRenderMode : true,
maximumRenderTimeChange : Infinity
});
viewer.scene.debugShowFramesPerSecond = true;
显式渲染一个帧
在应用代码中,如果一个变化出现但是不在上述情况列表中,那么需要立即显式渲染一个新的帧来展示改变。否则直到一个新的帧渲染这个改变也看不出来。
通过调用 Scene.requestRender 来显式渲染一个帧。
// Hides the stars
scene.skyBox.show = false;
// Explicitly render a new frame
scene.requestRender();
例如,一个应用可能具有设置场景属性的用户界面或者在场景中有某个对象,这种情况下,当用户界面状态改变时需要进行显式渲染。
更新/渲染循环事件
作为这些改变的一部分,我们已经把一些更新和渲染的逻辑分开。取决于应用目标帧的速率(默认是60FPS,参考 Viewer.targetFrameRate)Cesium总是通过同一速率更新。当使用 requestRenderMode时,只有当新的帧渲染时渲染函数才会被调用。我们重构了渲染循环事件的顺序,细节可以在下面看到。一般来说,在更新事件中调用 Scene.requestRender(或自动引起渲染的其他改变,比如相机位置)会为下一个帧及时将渲染加入到队列中。
scene.postUpdate.addEventListener(function() {
// This code will run at 60 FPS
if (changeToPromptRender) {
scene.requestRender();
}
});
scene.preRender.addEventListener(function() {
// This code will run when a new frame is rendered
// including when changeToPromptRender is true
});
preUpdate
事件在更新/渲染循环开始时发生,在场景内容更新渲染之前。这个事件在每次更新/渲染时以目标帧速率触发。
postUpdate
事件在场景更新之后发生,但是在一个新的帧将要渲染之前。这个事件在每次更新/渲染时以目标帧速率触发。
preRender
事件在一个新的帧渲染之前发生,但是在实际更新之后。当 requestRenderMode 开启时,这个事件只在新的帧渲染时触发。
postRender
事件在更新/渲染循环末尾发生,并且在新帧渲染之后,但是在更新完成之前。当 requestRenderMode 开启时,这个事件只在新的帧渲染时触发。
使用场景和举例
沙箱中的Scene Rendering Performance 在下拉列表中展示了通常会使用的一些例子,并且包含了一些不需要显式渲染的例子,当然也包括需要在哪里进行显式渲染的例子。
让我们用鼠标划过来获取实体状态的应用案例来说明如何提升性能。下面的代码展示了如何创建场景、添加实体、创建获取函数。在每次 MOUSE_MOVE事件触发时我们可能需要调用 requestRender,但这不是必须的,除非实体的属性发生改变。
// Create a viewer that won't render a new frame unless
// updates to the scene require it.
var viewer = new Cesium.Viewer('cesiumContainer', {
requestRenderMode : true,
maximumRenderTimeChange : Infinity
});
// Add an entity to the scene.
var entity = viewer.entities.add({
position : Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883),
box : {
dimensions : new Cesium.Cartesian3(1000000.0, 1000000.0, 30000.0),
material : Cesium.Color.CORNFLOWERBLUE
}
});
// If the mouse is over the billboard, change its scale and color,
// then request a new render frame.
var lastPicked;
handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
handler.setInputAction(function(movement) {
var pickedObject = scene.pick(movement.endPosition);
if (Cesium.defined(pickedObject) && (pickedObject.id === entity)) {
if (Cesium.defined(lastPicked)) {
return;
}
entity.box.material = Cesium.Color.YELLOW;
scene.requestRender();
lastPicked = pickedObject;
} else if (Cesium.defined(lastPicked)) {
entity.box.material = Cesium.Color.CORNFLOWERBLUE;
scene.requestRender();
lastPicked = undefined;
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);