【问题标题】:Unable to capture data from Back Buffer (DirectX9)无法从后台缓冲区 (DirectX9) 捕获数据
【发布时间】:2012-10-19 14:32:16
【问题描述】:

我正在尝试找到最快的截屏方法。到目前为止,我已经发现可以使用 GDI 或 DirectX。使用 GDI 我能够在 35 毫秒内捕获屏幕,而使用 DirectX 前端缓冲区平均需要 73 毫秒。我想要比这更快的方法。为此,在 DirectX 中捕获 Back Buffer 似乎是一个好方法。我正在使用以下代码来做同样的事情:

    D3DDISPLAYMODE  ddm;
    D3DPRESENT_PARAMETERS   d3dpp;

    if((g_pD3D=Direct3DCreate9(D3D_SDK_VERSION))==NULL)
    {
        ErrorMessage("Unable to Create Direct3D ");
        return E_FAIL;
    }

    if(FAILED(g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&ddm)))
    {
        ErrorMessage("Unable to Get Adapter Display Mode");
        return E_FAIL;
    }

    ZeroMemory(&d3dpp,sizeof(D3DPRESENT_PARAMETERS));

    d3dpp.Windowed=WINDOW_MODE;
    d3dpp.Flags=D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
    d3dpp.BackBufferFormat=ddm.Format;
    d3dpp.BackBufferHeight=nDisplayHeight=gScreenRect.bottom =ddm.Height;
    d3dpp.BackBufferWidth=nDisplayWidth=gScreenRect.right =ddm.Width;
    d3dpp.MultiSampleType=D3DMULTISAMPLE_NONE;
//  d3dpp.SwapEffect=D3DSWAPEFFECT_DISCARD;
    d3dpp.SwapEffect=D3DSWAPEFFECT_COPY;
    d3dpp.hDeviceWindow=hWnd;
    d3dpp.PresentationInterval=D3DPRESENT_INTERVAL_DEFAULT;
    d3dpp.FullScreen_RefreshRateInHz=D3DPRESENT_RATE_DEFAULT;

    if(FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hWnd,D3DCREATE_SOFTWARE_VERTEXPROCESSING ,&d3dpp,&g_pd3dDevice)))
    {
        ErrorMessage("Unable to Create Device");
        return E_FAIL;
    }

    if(FAILED(g_pd3dDevice->CreateOffscreenPlainSurface(ddm.Width, ddm.Height, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &g_pSurface, NULL)))
    {
        ErrorMessage("Unable to Create Surface");
        return E_FAIL;
    }

g_pd3dDevice->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO,&g_pSurface);
D3DXSaveSurfaceToFile("d:\\v\\temp.bmp",D3DXIFF_BMP,g_pSurface,NULL,NULL);

问题是,输出文件大小为 5MB,但输出中只显示黑色。在相同的代码中,如果我使用 GetFrontBufferData 我可以成功捕获屏幕。我做错什么了吗?请帮帮我..

【问题讨论】:

  • 除非我遗漏了一些东西,否则你实际上并没有画任何东西,所以你没有什么可以捕捉的。
  • @mots:是的,我有。我也在尝试,因为作者无法让它工作。
  • 您可以使用后台缓冲区来捕获您自己的窗口。但是,您必须先绘制一些东西。如果您尝试捕获操作系统屏幕,那么 DX 不是方法。另请注意,使用复制语义交换前后缓冲区的速度较慢。在调用 Present 之前读取后台缓冲区。
  • @Eisen:我可以想到两种使用 GDI 的方法。第一种是将屏幕捕获到剪贴板,然后读取剪贴板。您可以在这里找到一个示例(应该工作没有亲自测试):planetsourcecode.com/vb/scripts/… 第二种方法是使用带 NULL 的 GetWindowDC 获取整个屏幕的 DC,然后继续创建您自己的兼容 DC BitBlt int 该 DC 并阅读来自它的 BITMAP。

标签: c++ visual-studio-2008 visual-c++ gdi directx-9


【解决方案1】:

您无法从后台缓冲区捕获屏幕截图。

frontbuffer-backbuffer 的关系是这样工作的:backbuffer 被擦除,然后一次一个draw-call 将场景绘制到它上面。一旦它准备好,它就会与前端缓冲区一起翻转,这样前端缓冲区就会显示最终图像,而后端缓冲区会再次被擦除。

理论上,后台缓冲区有你想要的数据,但我不相信你可以可靠地获得这种状态。前端缓冲区就是为这个任务而设计的。

【讨论】:

  • 我明白了.. 但是在 codeproject 上有人说可以使用后台缓冲区捕获屏幕。我正在开发一个性能关键的应用程序,因此需要一种最佳方法。我正在探索可能的解决方案。
  • 后台缓冲区不会被擦除 - 他指定了复制语义。他只是没有画任何东西。
【解决方案2】:

我是这样截屏的:

IDirect3DSurface9 *offscreenSurface = 0;
d3dDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &offscreenSurface);
D3DXSaveSurfaceToFile( filename, D3DXIFF_BMP, offscreenSurface, 0, 0 )

请注意,此代码是在 Present 被调用之前运行的。

【讨论】:

  • 我尝试使用您建议的方法。它为所有内容返回黑屏。 Here 是我的问题的链接。我尝试根据您的建议将代码更改为: IDirect3DSurface9* pbackBuffer = 0; d3ddev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pbackBuffer); D3DXSaveSurfaceToFile(L"screen.bmp", D3DXIFF_BMP, pbackBuffer, NULL, NULL); pbackBuffer->Release();但现在它为每个应用程序返回一个黑色图像。以前的代码在其他任何地方都可以正常工作,但只是没有捕获全屏游戏。我在这里做错了什么?
  • 所以我注意到我的屏幕截图是在调用 Present() 之前拍摄的,而在你的情况下它们是在之后拍摄的。这可能与您的问题有关。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-25
  • 2015-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多