Direct3D:从IDirect3DSurface9(默认池)获取系统内存位图的有效方法是什么?

5
我有一个IDirect3DSurface9,默认池,YUV格式。我如何高效地从中获取位图位?目前我:
    创建渲染目标:
    device->CreateRenderTarget(surf_desc.Width, surf_desc.Height, D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, 0, TRUE, &render_target, NULL)
    将YUV转换为RGB32:
    device->StretchRect(videomem_surf, NULL, render_target_, NULL, D3DTEXF_NONE)
    (完整矩形,无拉伸)
    在系统内存中创建普通离屏表面
    device->CreateOffscreenPlainSurface(surf_desc.Width, surf_desc.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &sysmem_offscreen_surf, NULL)
    从显存中复制数据到系统内存:
    device->GetRenderTargetData(render_target, sysmem_offscreen_surface)
    从离屏表面获取DC,创建兼容的DC和兼容的位图,从离屏表面DC BitBlt到兼容的DC,并通过GetDIBits()将位图位复制到我的缓冲区

这看起来有点繁琐,因为需要进行许多复制:从原始表面到渲染目标,然后到离屏表面,然后到兼容位图,最后到我的缓冲区。如何改进这个过程?

谢谢

2个回答

4

正如interjay所指出的那样... 你几乎在以“正确”的方式进行操作。

显而易见的改进方法是只调用CreateRenderTarget和CreateOffscreenPlainSurface一次,然后多次重复使用它们。最快获取位图的方法是直接锁定表面(LockRect)。

此外,如果您需要在类似视频的实时环境中执行此操作,最好设置一个表面数组(两种类型)。您可以将多个YUV帧加载到CreateRenderTarget表面上,然后在填充完数组后将第一个复制到OffscreenPlainSurface并锁定它。

这样可以允许更多的命令进行流水线处理,并避免调用锁定操作并强制同步管道(即,在继续之前,您正在锁定的表面必须准备就绪,这会导致管道同步)。


3

由于您在创建渲染目标时使用了可锁定模式(CreateRenderTarget的第6个参数),因此您可以使用LockRect来锁定渲染目标,并直接从中复制数据。

MSDN不建议使用可锁定的渲染目标,并说:

如果需要读取渲染目标,请使用GetRenderTargetData而不是可锁定的渲染目标。

因此,一种替代方法是调用GetRenderTargetData到离屏表面,然后锁定离屏表面(而不是使用DC和位图)。


LockRect提供哪种数据格式?没有找到任何描述。与GetDIBits()相同吗? - Andriy Tylychko
@robin:每一行像素都根据颜色格式打包了字节。因此,对于D3DFMT_A8R8G8B8,每个像素将有4个字节。每个像素的字节按相反的顺序(小端)排列,因此在这种情况下,它们的顺序为:B,G,R,A。给定给您的“Pitch”值告诉您每行需要多少字节。您需要知道这一点,因为每行可能有未使用的字节。 - interjay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接