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..

Eisen Jack
  • 179
  • 3
  • 12
  • 1
    Did you check: http://stackoverflow.com/questions/12879880/how-to-get-the-screenshot-of-the-desktop-from-back-buffer-with-directx – mots_g Oct 30 '12 at 09:27
  • Unless I'm missing something you are not actually drawing anything so you have nothing to capture. – Sebastian Cabot Oct 30 '12 at 09:29
  • @mots: Yeah I have. I am trying that too, given that the author couldn't get it to work. – Eisen Jack Oct 30 '12 at 10:08
  • @Sabastian: Is that so? I am really new to DirectX. – Eisen Jack Oct 30 '12 at 10:08
  • 1
    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. – Sebastian Cabot Oct 30 '12 at 10:41
  • Can you please suggest me some good method to capture the screen then pls? – Eisen Jack Oct 30 '12 at 11:11
  • 1
    @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): http://planetsourcecode.com/vb/scripts/ShowCode.asp?txtCodeId=10754&lngWId=3 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. – Sebastian Cabot Oct 30 '12 at 11:32
  • @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. – Eisen Jack Oct 30 '12 at 12:53

2 Answers2

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.

Ian
  • 450
  • 4
  • 18
  • 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. – Eisen Jack Oct 30 '12 at 09:59
  • 2
    The back buffer will not get wiped - He specified copy semantics. He is just not drawing anything. – Sebastian Cabot Oct 30 '12 at 10:42
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.

default
  • 2,637
  • 21
  • 44
  • I tried using your suggested method. It returns a black screen for everything. [Here](http://stackoverflow.com/q/31286503/4794952) 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? – newbie2015 Jul 08 '15 at 18:53
  • 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. – default Jul 08 '15 at 20:27