Thread: Screenshots in DX9

  1. #1
    Registered User
    Join Date
    Aug 2003
    Posts
    288

    Screenshots in DX9

    well, im wondering how i could take a screenshot of the DirectX window and save it to a bmp (for simplicity). At the moment, heres the code i use:

    Code:
    bool TakeScreenShot(IDirect3DDevice9 *Device, char *FileName, int ScreenX, int ScreenY)
    {
      IDirect3DSurface9 *FrontBuffer; 
      bFailed = false;
      HRESULT Result;
    
      Result = Device->CreateOffscreenPlainSurface(ScreenX, ScreenY, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &FrontBuffer, NULL);
    
      if (Result != D3D_OK)
      {
        //FrontBuffer->Release(); Thanks Magos
        return false;
      }
    
      Result = Device->GetFrontBufferData(0, FrontBuffer);
    
      if (Result != D3D_OK)
      {
        FrontBuffer->Release();
        return false;
      }
    
      Result = D3DXSaveSurfaceToFile(FileName, D3DXIFF_BMP, FrontBuffer, NULL, NULL);
    
      if (Result != D3D_OK)
      {
        FrontBuffer->Release();
        return false;
      }
    
      FrontBuffer->Release();
      return true;
    }
    it works fine, but its EXTREMELY slow, i tried putting it in a thread, and i get a deformed image.

    so, i was wondering, would it be faster/better to do this using GDI? ie. bitblt into a memory hdc and go from there? or is this the only way?

    Thanks in advance.
    Last edited by X PaYnE X; 04-14-2005 at 05:26 AM.

  2. #2
    ---
    Join Date
    May 2004
    Posts
    1,379
    I'm not sure, but have you noticed when pressing a game specific button to print a screenshot, most games pause slightly. So maybe you are asking too much of your system.

  3. #3
    Registered User
    Join Date
    Aug 2003
    Posts
    288
    yeah they pause.. slightly, if i try, i pause for a few seconds, in some games, the pause is barely noticeable.

  4. #4
    Confused Magos's Avatar
    Join Date
    Sep 2001
    Location
    Sweden
    Posts
    3,145
    If CreateOffscreenPlainSurface() fails, FrontBuffer will most likely be NULL (perhaps even undefined). You shouldn't call FrontBuffer->Release(); on it!
    MagosX.com

    Give a man a fish and you feed him for a day.
    Teach a man to fish and you feed him for a lifetime.

  5. #5
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    1. Lock the front buffer.

    Code:
    DWORD SizeInDWORDS=0;
    
    asm {
      cld
      mov  esi,[Buffer_Ptr]
      mov  edi,[FileBuffer_Ptr]
      
      mov  ecx,[BufferPitch]
      mul   ecx,[ScreenHeight]
     
      mov  SizeInDWORDS,ecx
    
      rep   movsd
      and ecx,03h
      rep movsb
    }
    
    int handle=_open("screenshot.dat",_O_BINARY | _O_IREAD,_S_IREAD );
    
    if (handle) 
    {
      write(handle,(DWORD *)FileBuffer_Ptr,(SizeInDWORDS<<2));
      close(handle);
    }
    This shouldn't be too slow as the memcpy is super fast. The disk access cannot be sped up and depends on the condition, type, size, and quality of the hard drive. But C block writes are extremely fast operations. You will notice a small pause but it shouldn't be too long at all. You could pre-compute the buffer size and then use that value instead of computing it inside of the function. The formula is height*BufferPitch+width.

    This will write out raw RGB values but you could alter the code to support any format you want.

  6. #6
    Registered User
    Join Date
    Aug 2003
    Posts
    288
    how exactly would i lock the front buffer? i know how its done in DirectX 7 but not in DirectX 9.

    and as for the code you provided, its basically just copying the contents of the locked front buffer to a file, right? or am i reading it wrong.

  7. #7
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Yes that's all it is.

    Locking the front buffer is done by gaining access to the swap chain. Look at the IDirect3DDevice9 interface and check the functions. Once you have access to the swap chain then you can lock the front buffer from there because the swap chain is a COM interface with functions as well. You may have to specify that you want a lockable front buffer when you create your application, however. When I used my code to lock the back buffer you had to specify you wanted a lockable back buffer or the call would fail in every case. This is done in the D3DPRESENT_PARAMETERS structure using the flags member. It looks like from the SDK though that you do not have to specify a lockable front buffer, only if you want to lock the back buffer.

  8. #8
    Registered User
    Join Date
    Aug 2003
    Posts
    288
    I just read throught the documentation and this is what i changed:

    Code:
    IDirect3DSurface9 *FrontBuffer; 
    	HRESULT hResult;
    	char sBuffer[256];
    
    	/*hResult = g_Device->CreateOffscreenPlainSurface(g_DisplayMode.Width, g_DisplayMode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &FrontBuffer, NULL);
    
    	if (hResult != D3D_OK)
    	{
    		FrontBuffer->Release();
    		return false;
    	}*/
    
    	hResult = g_Device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &FrontBuffer);
    the reason i commented the first half is i was unsure if you even needed to create a surface, this works, but its even slower now.

  9. #9
    Registered User
    Join Date
    Apr 2002
    Posts
    1,571
    Have you thought about using the D3DX API D3DXSaveSurfaceToFile ? An example of usage can be found here:

    http://www.mvps.org/directx/articles/screengrab.htm

    edit: Oops, I kind of skimmed your first post to be honest. Sorry about that. Also, I don't think you can get much faster than that. The nature of what you are doing is painfully slow.
    Last edited by MrWizard; 04-14-2005 at 11:50 AM.
    "...the results are undefined, and we all know what "undefined" means: it means it works during development, it works during testing, and it blows up in your most important customers' faces." --Scott Meyers

  10. #10
    Registered User
    Join Date
    Aug 2003
    Posts
    288
    i already used it... first post

    Code:
    Result = D3DXSaveSurfaceToFile(FileName, D3DXIFF_BMP, FrontBuffer, NULL, NULL);
    ...

  11. #11
    Registered User
    Join Date
    Aug 2003
    Posts
    288
    then how is it that in other games, its extremely fast? or atleast faster then what i have here?

  12. #12
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    I'm not sure this will help any but if you need the surface and you want to support screenshots then create the surface at load time. This way you don't create it on the fly. You may also be able to do something else. Instead of doing a blit, just change the render target to your off-screen surface and write that surface to file. When done, set the render target back to the original surface.

    You may want to fire off a thread as well to handle this, but I still think you will get some kind of pause in the game.

  13. #13
    Registered User
    Join Date
    Aug 2003
    Posts
    288
    yeah, thats a good idea. although im not sure how i would change the render target? unless theres a function (ill search the documentation now). Thanks for the idea

  14. #14
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Device->SetRenderTarget()

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Screenshots
    By Dae in forum Windows Programming
    Replies: 4
    Last Post: 07-08-2009, 01:21 AM
  2. Lights and .X files in c# using dx9
    By poopy_pants2 in forum Game Programming
    Replies: 2
    Last Post: 04-26-2004, 10:30 AM
  3. DX9 and Dev-C++
    By Ashes999 in forum Windows Programming
    Replies: 5
    Last Post: 06-10-2003, 12:19 AM
  4. screenshots of your games
    By stallion in forum Game Programming
    Replies: 94
    Last Post: 03-26-2003, 08:56 PM
  5. Screenshots of Upcoming "Mists of Avalon"
    By harry_p in forum C++ Programming
    Replies: 62
    Last Post: 08-02-2002, 11:37 AM