Unable to capture data from Back Buffer (DirectX9)
Asked Answered
C

2

2

I am trying to find the fastest method to take the screenshot. So far I've figured out that either GDI, or DirectX can be used. Using GDI I am able to capture the screen in 35ms, while using DirectX Front buffer it takes 73ms average. I want even faster method than this. For this purpose capturing Back Buffer in DirectX seems to be a good method. I am using the following code to do the same:

    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);

The problem is, that the output file measures 5MB, but shows nothing but black color in the output. While in the same code if I use GetFrontBufferData I can successfully capture the screen. Am I doing anything wrong? Please help me..

Culinarian answered 30/10, 2012 at 9:15 Comment(8)
Did you check: #12880380Gwalior
Unless I'm missing something you are not actually drawing anything so you have nothing to capture.Valero
@mots: Yeah I have. I am trying that too, given that the author couldn't get it to work.Culinarian
@Sabastian: Is that so? I am really new to DirectX.Culinarian
You can use the back buffer to capture YOUR own window. However you must draw something first. If you are trying to capture the OS screen then DX is not the way. Also notice that using copy semantics for swapping the back and front buffers is slower. Read the back buffer before calling Present.Valero
Can you please suggest me some good method to capture the screen then pls?Culinarian
@Eisen: There are two approaches I can think of using GDI. The first is to capture the screen to the clipboard and then read the clipboard. You can find a sample here (Should work didn't test it personally): planetsourcecode.com/vb/scripts/… The second approach is to get the DC for the entire screen using GetWindowDC with NULL and proceed to create your own compatible DC BitBlt int that DC and read the BITMAP from it.Valero
@Sabastian: Thanks a lot for good help, my friend. I've tried BitBlt and so far it's the method which works with best results, for me. I'll check out the other one as well. Thanks, I'm grateful.Culinarian
G
0

Here's how I take a screenshot:

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

Note that this code is run before Present is called.

Gae answered 14/2, 2013 at 23:8 Comment(2)
I tried using your suggested method. It returns a black screen for everything. Here is a link to my question. I tried changing the code as per your suggestion to this: IDirect3DSurface9* pbackBuffer = 0; d3ddev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pbackBuffer); D3DXSaveSurfaceToFile(L"screen.bmp", D3DXIFF_BMP, pbackBuffer, NULL, NULL); pbackBuffer->Release(); But now it returns a black image for every application. Previous code worked fine everywhere else but only didnt capture full screen games. What am I doing wrong here?Lineage
So I noticed my screenshots are taken before Present() is called, and in your case they are after. That may be related to your problem.Gae
T
1

You cannot capture a screen shot from the backbuffer.

The frontbuffer-backbuffer relationship works like this: the backbuffer is wiped, and then has the scene drawn to it one draw-call at a time. As soon as it's ready it's flipped with the frontbuffer so the frontbuffer gets the final image to display while the backbuffer is again wiped.

Theoretically there is a point where the backbuffer has the data you want, but I do not believe you can reliably get this state. The frontbuffer is designed for this task.

Trundle answered 30/10, 2012 at 9:44 Comment(2)
I see.. but on codeproject someone has stated that it is possible to capture screen using the back buffer. I am working on a performance critical application, and so need an optimal method. I am exploring the possible solutions.Culinarian
The back buffer will not get wiped - He specified copy semantics. He is just not drawing anything.Valero
G
0

Here's how I take a screenshot:

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

Note that this code is run before Present is called.

Gae answered 14/2, 2013 at 23:8 Comment(2)
I tried using your suggested method. It returns a black screen for everything. Here is a link to my question. I tried changing the code as per your suggestion to this: IDirect3DSurface9* pbackBuffer = 0; d3ddev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pbackBuffer); D3DXSaveSurfaceToFile(L"screen.bmp", D3DXIFF_BMP, pbackBuffer, NULL, NULL); pbackBuffer->Release(); But now it returns a black image for every application. Previous code worked fine everywhere else but only didnt capture full screen games. What am I doing wrong here?Lineage
So I noticed my screenshots are taken before Present() is called, and in your case they are after. That may be related to your problem.Gae

© 2022 - 2024 — McMap. All rights reserved.