您的位置:首页 > 其它

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

2015-08-30 12:19 295 查看
在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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: