在DirectX中进行全屏截图和渲染【性能】

5
我需要一种方法来获取屏幕数据并将其传递到我的应用程序中的DX9表面/纹理,并以至少1600 * 900分辨率下的25fps渲染它,30更好。
我尝试了位块传输,但即使在此之后,我也只有20fps,在加载数据到纹理并呈现它后,我只有11fps,这远远低于我所需的速度。
GetFrontBufferData不可行。 在这里是关于使用Windows Media API的内容,但我不熟悉它。示例直接保存数据到文件,也许可以设置为提供单个帧,但我没有找到足够好的文档以自己尝试。
我的代码:
m_memDC.BitBlt(0, 0, m_Rect.Width(),m_Rect.Height(), //m_Rect is area to be captured
               &m_dc, m_Rect.left, m_Rect.top, SRCCOPY); 
      //at 20-25fps after this if I comment out the rest

//DC,HBITMAP setup and memory alloc is done once at the begining
GetDIBits( m_hDc, (HBITMAP)m_hBmp.GetSafeHandle(),
    0L,             // Start scan line
    (DWORD)m_Rect.Height(),     // # of scan lines
    m_lpData,                   // LPBYTE
    (LPBITMAPINFO)m_bi,     // address of bitmapinfo
    (DWORD)DIB_RGB_COLORS);     // Use RGB for color table
     //at 17-20fps

IDirect3DSurface9 *tmp;
m_pImageBuffer[0]->GetSurfaceLevel(0,&tmp); //m_pImageBuffer is Texture of same 
                                            //size as bitmap to prevent stretching
hr= D3DXLoadSurfaceFromMemory(tmp,NULL,NULL,
                             (LPVOID)m_lpData,
                             D3DFMT_X8R8G8B8,
                             m_Rect.Width()*4,
                             NULL,
                             &r,                 //SetRect(&r,0,0,m_Rect.Width(),m_Rect.Height();
                             D3DX_DEFAULT,0);
 //12-14fps
IDirect3DSurface9 *frameS;
hr=m_pFrameTexture->GetSurfaceLevel(0,&frameS); // Texture of that is rendered
pd3dDevice->StretchRect(tmp,NULL,frameS,NULL,D3DTEXF_NONE);
//11fps

我发现对于512*512的正方形,它以30fps运行(例如490*450在20-25),所以我尝试分割屏幕,但似乎效果不佳。 如果代码中有遗漏,请务必写下来,而不是投票反对。谢谢。

出于好奇,GetFrontBufferData有什么问题吗?我过去使用它取得了一些成功(尽管做的事情略有不同)。 - Daniel Sloof
Daniel Sloof:最好只有3 FPS,文档中也说明它是一个慢函数。 - LeonidasCZ
2个回答

12

从Windows 8开始,有一个新的桌面复制API ,可用于捕获屏幕在视频内存中的图像,包括鼠标光标的更改和实际更改或移动的屏幕部分。这比现有的任何GDI或D3D9方法都要高效得多,并非常适用于将桌面编码为视频流,因为您永远不必从GPU内存中取出纹理。通过枚举DXGI输出并在要捕获的屏幕上调用DuplicateOutput即可使用新API。然后,您可以进入等待屏幕更新并按顺序获取每个帧的循环。

要将帧编码为视频,我建议查看Media Foundation。具体来说,请查看Sink Writer,以了解最简单的视频帧编码方法。基本上,您只需将每个视频帧获取的D3D纹理包装到IMFSample对象中。这些可以直接传递到Sink Writer中。有关更多信息,请参见MFCreateDXGISurfaceBufferMFCreateVideoSampleFromSurface函数。通常,为了获得最佳性能,您需要使用像H.264这样具有良好硬件编码支持的编解码器(在大多数计算机上)。

为了充分披露,我在微软负责拥有桌面复制API的团队工作,并且我个人撰写了使用该技术将桌面(以及视频、游戏等)捕获到60fps视频文件中的应用程序,以及许多其他场景。这也被用于进行屏幕流媒体、远程协助以及在微软内部进行大量操作。


6
您能否提供一个示例,说明如何从dxgi表面连接到一个文件写入器汇(或其他可行的选项),该选项在GPU上进行H.264压缩,并仅使用CPU来编写流?网上似乎没有有效的示例。毫无疑问,这是一个快速的API,但如果没有简单的示例,花费数月时间才能理解它。 - thewhiteambit
1
全新的StackOverflow.documentation是一个非常好的地方,可以上传像@thewhiteambit所询问的示例。 - ANeves

1

如果您不喜欢前缓冲区,请尝试后缓冲区:

LPDIRECT3DSURFACE9  surface;
surface = GetBackBufferImageSurface(&fmt);

要将其保存到文件中,请使用

D3DXSaveSurfaceToFile(filename, D3DXIFF_JPG, surface, NULL, NULL);

应用程序不是全屏模式,我无法访问原始渲染目标,因此后备缓冲区仅为我的应用程序区域。我需要捕获整个桌面/覆盖其上的任何内容。 - LeonidasCZ

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