Feb 6

关于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,没有发现有效率的提升,如果有那位看到此博客的朋友知道原因,希望不吝赐教。

to "关于BeginScene和EndScene函数"

  1. cywater2000 Says:

    你只要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.”

    siney 于 2009-02-07 10:55:10 回复
    目前我也是这样认为的,这也是dxsdk文档里唯一一段对此的描述,我在文章中也写了,一般会在EndScene和Present之间插入一些计算代码,来保证EndScene之后不会立刻等待gpu绘图,而且还是无法解释Farcry2 会什么存在空的
    Begin/End调用这个问题。
    cywater2000 于 2009-02-07 12:39:56 回复
    Begin/End这2个函数是必须调用的,但中间的Draw却是根据逻辑决定的。所以有空的很正常。

  2. siney Says:

    恩, 暂时先这样理解吧。

  3. xoyojank Says:

    要是用了ID3DXRenderToSurface就会这样的

  4. xingzhe2001 Says:

    这是由于如果begin和end之间渲染命令太多,导致d3d的runtime的command buffer满了,会引起核心模式和用户模式的切换,可能是影响性能吧。

  5. xingzhe2001 Says:

    参考这片文章http://www.cnblogs.com/effulgent/archive/2009/02/10/1387438.html

    begin和end之间渲染命令太多导致后画的资源在显存中不能常驻,导致不必要的内存传输。

  6. effulgent Says:

    个人猜测: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在渲染过程的哪里,只能猜下)。

Leave a Reply