新版Ogre的帧监听器(FrameListener)新加了一个方法,frameRenderingQueued,查看例子后发现,原先的frameStarted基本都被这个方法所代替了,决定打开源代码看看Ogre的意图。
我们从renderOneFrame开始分析,这个方法只有三句话。
1
bool
Root::renderOneFrame(
void
)
2
{ 3 if ( ! _fireFrameStarted()) 4 return false ; 5 6 if ( ! _updateAllRenderTargets()) 7 return false ; 8 9 return _fireFrameEnded(); 10 }
11
1. 触发所有FrameListener 的frameStarted
2. _updateAllRenderTargets
3. 触发所有FrameListener 的frameEnded
这个过程很清晰,不再多说,并没有发现frameRenderingQueued 调用。接着进入_updateAllRenderTargets ,这个方法也做三件事。
bool
Root::_updateAllRenderTargets(
void
)
{ // update all targets but don't swap buffers mActiveRenderer -> _updateAllRenderTargets( false ); // give client app opportunity to use queued GPU time bool ret = _fireFrameRenderingQueued (); // block for final swap mActiveRenderer -> _swapAllRenderTargetBuffe rs(mActiveRenderer -> getWaitForVerticalBlank()); return ret; }
1. 更新所有渲染目标(不翻转)
2. 触发所有FrameListener 的frameRenderingQueued (发现目标了^_^ )
3. 翻转所有渲染目标
由此可见,frameRenderingQueued 的调用时机是在frameStarted 的后面,frameEnded 的前面,而且在frameStarted 和frameRenderingQueued 还有一个更新所有渲染目标(不翻转),经过这样分析,调用关系就清楚了,简单归纳为下列五步:
1. 触发所有FrameListener 的frameStarted
2. 更新所有渲染目标(不翻转)
3. 触发所有FrameListener 的frameRenderingQueued
4. 翻转所有渲染目标
5. 触发所有FrameListener 的frameEnded
通过分析后已经可以看明白调用过程了,接下来进一步说说,为什么要加入frameRenderingQueued 呢,一个方法的加入总归是有原因的,大家都很忙,Ogre 团队不会浪费时间加一个没用的东西。首先看原来用的frameStarted ,可以看到frameStarted 的调用时机是最靠前的,接下来是更新所有渲染目标(不翻转),然后是frameRenderingQueued 。可以看到,frameStarted 和frameRenderingQueued 的主要区别就在于更新所有渲染目标前调用还是后调用。如果把逻辑放在frameStarted 中,那么更新所有渲染目标这个操作必然在其之后;反之把逻辑放在frameRenderingQueued 中,则逻辑执行在更新所有渲染目标之后,那为什么要放在后面,而不是放在前面呢?原因在于渲染是由GPU 来完成的,更新所有渲染目标这个操作返回时,GPU 需要一定的时间来完成当前的渲染,这样当执行frameRenderingQueued 时,相当于逻辑和GPU 在并行计算,CPU 也没有闲着,这样就提高了效率,效率很重要,不是么,^_^ 。
这里还需要指出的一点是,由于frameRenderingQueued 执行时,GPU 已经在渲染上一帧内容了,因此写在frameRenderingQueued 里的逻辑将在下一帧才能够在渲染中体现出来,一般来说,这个问题是无关紧要的。