DX转存速率
DX的诞生之初虽然是为了提升游戏开发效率的,但由于游戏的表现基本上都是依赖显卡来实现的,从这个角度来看DX实际上也是在操纵显卡,更进一步地来说,游戏中的Surface或者说图像不过是一个二维数组数据,DX就是在处理这一块一块的二维矩阵。就像CUDA那样,我们也可以将普通数据组织成这样的二维数据并交予DX当作Surface或者图像来处理,利用现代显卡强大的性能就可以处理大量数据。
当使用DX在显卡端处理完数据后,再将其从显存中下行到内存中交由CPU进行Encode,就可以得到一个类似Fraps的游戏录制工具。
就目前的硬件构架来说,数据从显存中下行到内存中需要经过主板的PCI总线(不知道Intel的i5处理器将南桥芯片和PCI总线合二为一后会不会提升数据传输的速度?),这个过程肯定会有时间上的消耗的。通过Wiki上查询目前主流PCI总线的数据传输速率(AGP 8x是2.1GB/s和PCI-e 16x是4GB/s),发现即使是1080p,60fps的高清片源(1920*1080*12/8*60=178MB/s),也基本上可以满足数据传输的要求。但是实际编码过程中发现,这个过程中花费的时间远远大于想象中数值,到底是为什么呢?
后来跟踪代码发现,性能的消耗主要花费在IDirect3DSurface9::LockRect函数之上。这个函数可以将显存上的数据拷贝到内存的一段Buffer上,诚然,这个过程的确要花费一些时间,但不应该有那么长时间的消耗的!
问题出现在DX对数据的分配和管理之上:
http://members.gamedev.net/jhoxley/directx/DirectXForumFAQ.htm#D3D_13
在这篇文章中详细地解析了DX对内存数据的分配和管理,中文的翻译版可以参考这里。
因为之前一直以为是由于主板总线限制了显存数据到内存数据的传输速率,因而导致整体速率的下降,但是通过上面文章的解释:
最终就是强调一点,锁定操作是很慢的.大多数情况下祸端不是由带宽引起(指的是VRAM到System RAM的传送–译注),而是源于阻塞所带来的延迟.
可以看出真正导致数据传输率极大降低的罪魁祸首是由于Lock操作时CPU和GPU时钟序列不同步而导致的阻塞。
那么有没有解决的方法呢?
在网上有一些利用DX截屏的方法,其中就有一种解决方法,就是创建"离屏(Offline)"的Surface(在系统内存中分配资源),然后将原来的显存数据(RenderTarget或者Surface)拷贝到这个Offline Surface中,然后直接对这个Offline Surface进行Lock的操作,由于对这种离屏的Surface进行Lock的操作并不会涉及到CPU的锁机理,即这个Offline Surface中的数据始终是Lockedable的,对它进行Lock后GPU并不需要去等待CPU同步,于是这样就节省了大量的时间。
重要代码:
LPDIRECT3DSURFACE9 surf;
if (FAILED(hr = lpDevice->CreateOffscreenPlainSurface(mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surf, NULL))) //注意第四个参数不能是D3DPOOL_DEFAULT
{
return hr;
}
// 接下来调用IDirect3DDevice9::GetRenderTargetData将数据拷贝到surf中
…
surf->LockRect(…);