获取Ogre或D3D的渲染结果的效率问题

在Ogre中获取渲染结果可以使用RenderTarget的copyContentsToMemory方法, 例:

char* src = new char[mWindow->getWidth() * mWindow->getHeight() * 4];
Ogre::PixelBox* pixbox = new Ogre::PixelBox(mWindow->getWidth(), mWindow->getHeight(), 1, Ogre::PF_X8R8G8B8, src );
mWindow->copyContentsToMemory(*pixbox, Ogre::RenderTarget::FB_AUTO);

mWindow为当前渲染窗口RenderWindow,如果使用RTT(渲染到纹理)也是同样的处理

需要注意的是导出格式应该是PF_X8R8G8B8,避免格式转换带来性能损耗

 

不幸的是,实际中发现这个方法出奇的慢

一个上百帧的场景竟然因为这一句话下降到了二三十帧

一般来说从GPU显存将数据复制到CPU内存是个很慢的过程,但这么慢也实在是太夸张了

 

查看copyContentsToMemory的实现,大概是下面的样子(做了简化):

 void D3D9Device::copyContentsToMemory(const PixelBox &dst)
    {
       IDirect3DSurface9 *surface = NULL;
           D3DLOCKED_RECT lrect; 
 
           mDevice->CreateOffscreenPlainSurface(width, height, format, D3DPOOL_SYSTEMMEM, surface, 0);
 
           IDirect3DSurface9 *backSurface;
           mDevice->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO,&backSurface);
           mDevice->GetRenderTargetData(backSurface, surface);
 
           surface->LockRect(&lrect, NULL,  D3DLOCK_READONLY);
           memcpy(dst.data, lrect.pBits, dst.getWidth() * dst.getHeight()*4);
           surface->UnlockRect();
surface
->Release(); tmp->Release(); }

大意是在内存(D3DPOOL_SYSTEMMEM)中创建离屏表面

然后将后缓冲中数据复制到该表面中(GetRenderTargetData)

最后锁定该表面获取内存指针完成数据复制

其中完成将数据从GPU显存复制到CPU内存的步骤为GetRenderTargetData

这么实现可以说没有任何问题,简直就是标准做法

 

测试了一下发现GetRenderTargetData方法用时为0毫秒,也就是说足够的快

实际瓶颈出在LockRect方法上,测试场景中每次调用用时达到30多毫秒

原则上来说,该表面在内存中创建,并没有在场景中使用,锁定不应该有任何消耗

直接使用D3D测试时,也发现该方法用时为0,实在想不通是什么原因

...

 

一番周折,最后想到一个非常规的解决办法:

仅在第一次创建离屏表面时锁定、解锁以获取数据区指针,之后直接利用该指针完成数据复制

这么做之所以成立是因为创建的离屏表面实际上也并不需要锁定

每次只要调用GetRenderTargetData将数据从GPU显存复制出来即可

 

使用Ogre的话,实现这个稍微有点麻烦,因为Ogre并不支持直接访问Direct3D

需要修改Ogre和RenderSystem_Direct3D9插件的代码

不过这是目前个人想到的解决这个问题的最好办法了

 

注:Ogre中可以直接使用D3D,不需要修改Ogre源码,参考下一篇:在Ogre中直接使用D3D

posted @ 2015-08-30 12:19  wiki3D  阅读(1601)  评论(0编辑  收藏  举报