关于BeginScene和EndScene函数
昨天闲来没事,特地跟踪了下Farcry2引擎的渲染流程,无意间发现Farcry2每帧竟然会多次调用BeginScene和EndScene函数,我以前的认识是每帧开始的时候BeginScene,Present之前EndScene,一般为了gpu和cpu的并行性,会在EndScene和Present函数之间插入一些计算代码(比如物理计算等),让cpu不至于等待gpu绘图。
很多人可能会认为在SetRenderTarget之后要BeginScene,使用完RenderTarget后要EndScene,网络上有很多例子也是这样写的,但这是完全错误的,渲染RenderTarget不需要Begin和End,否则会导致D3D_ERROR,如下:
Direct3D9: (ERROR) :BeginScene, already in scene. BeginScene failed.
就是说不允许连续两次的BeginScene调用,所以一般就是一帧调用一次Begin和End函数,而Farcry2非常奇怪,它存在如下函数调用顺序:
GetBackBuffer ->BackBuffer
SetRenderTarget
BeginScene
Some Draw Call
EndScene
BeginScene
Some Draw Call
EndScene
……
SetRenderTarget( BackBuffer )
BeginScene
Some Draw Call
EndScene
BeginScene
Some Draw Call
EndScene
……
Present
有时候甚至会出现一对空的
BeginScene
EndScene
中间什么也没干,我很好奇这些函数到底有什么特别的用法,我在DXSDK文档和google都搜索了,没有找到关于此的描述,不明白其中的原因,我猜想是不是这样可以提高cpu和gpu之间的并行率,但我以自己引擎来测试,把一些渲染代码放在一起Begin/End,没有发现有效率的提升,如果有那位看到此博客的朋友知道原因,希望不吝赐教。
2009-02-07 00:38:58 回复该留言
你只要render,就需要begin-end,当你把渲染逻辑分成多个小块的时候,就可能要用多个bengin-end。你把begin-end当成类似lock-unlock的话就好理解了。
为什么要多个短小的,而不是一个长范围的呢?比如
BeginScene
...
EndScene
Present
DX文档里说的很清楚:
“ To enable maximal parallelism between the CPU and the graphics accelerator, it is advantageous to call IDirect3DDevice9::EndScene as far ahead of calling present as possible.”
2009-02-07 13:40:49 回复该留言
恩, 暂时先这样理解吧。
2009-02-09 13:12:27 回复该留言
要是用了ID3DXRenderToSurface就会这样的
2009-07-24 11:19:05 回复该留言
这是由于如果begin和end之间渲染命令太多,导致d3d的runtime的command buffer满了,会引起核心模式和用户模式的切换,可能是影响性能吧。
2009-07-24 11:49:41 回复该留言
参考这片文章http://www.cnblogs.com/effulgent/archive/2009/02/10/1387438.html
begin和end之间渲染命令太多导致后画的资源在显存中不能常驻,导致不必要的内存传输。
2010-09-16 11:58:12 回复该留言
个人猜测:beginScene和endScene其着标号的作用,D3D RUNTIME根据这两个标号作了很多方面的渲染优化,比如COMMAND BUFFER的FLUSH,MANAGED RESOURCE的换进换出,BS和ES之间合理的渲染任务,可以高效的利用显示资源,BS和ES很有可能会让DRIVER提交绘制命令到GPU,过长的绘制块可能会导致GPU等待CPU任务提交,而PRESENT除了执行FLIP功能外,还可能会阻塞等待绘制结果(取决CB和DRIVER BUFFER的使用策略),而BS和ES后立即PRESENT肯定是效率低下的,至少在三帧内就会阻塞等待一次,所以会在PRESENT之前插入适量的CPU任务,而空的BS和ES很可能是为了FLUSH COMMAND BUFFER(不知道FARCRY2 空BS ES在渲染过程的哪里,只能猜下)。