0

Ok, so for anyone who's used it much - this should be a SUPER easy question.

I was just searching online for a way to use DirectX/Direct3D to take faster screenshots, and everyone was talking about GetFrontBufferData() and how wonderful it was.

I've pounded at it for a while but am starting to think they're using the term "screenshot" incorrectly... My call succeeds, but I never get a "screenshot".

So the question is, can you actually use GetFrontBufferData() to make a REAL screenshot of the whole desktop, or is it merely a way to read the pixels out of the front buffer WITHIN the confines of your d3d-device's drawing area?

(Upon success, I'd have expected to see the drawing area within my window do that old tv-in a tv-in a tv-in a tv-in a tv - kind of effect. I got nothing but black.)

Edit:

So, I was able to get a screenshot to work, but can't seem to put my image into the buffer for my app window.

At first, I thought this was because they were separate devices, but I've tried creating a second surface on the correct device and then manually copying the contents over. (Although I might be copying it wrong ((not important right now)) the call to the stretchrect still fails for the correct device.

Any idea why it won't let me apply this surface to my backbuffer???

D3DDISPLAYMODE          d3dDisplayMode;
D3DPRESENT_PARAMETERS   d3dPresentationParameters;

if( (d3d=Direct3DCreate9(D3D_SDK_VERSION))==NULL )
    exit(1);
D3DCAPS9 d3dcps;
d3d->GetDeviceCaps(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,&d3dcps);
DWORD targets = d3dcps.NumSimultaneousRTs;
// TODO: ^ make a way for user to select one from this and put it into i
DWORD i=D3DADAPTER_DEFAULT;

if( d3d->GetAdapterDisplayMode(i,&d3dDisplayMode)==D3DERR_INVALIDCALL )
    exit(1);

ZeroMemory(&d3dPresentationParameters,sizeof(D3DPRESENT_PARAMETERS));//Fills a block of memory with zeros.
d3dPresentationParameters.Windowed = TRUE;
d3dPresentationParameters.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
d3dPresentationParameters.BackBufferFormat = d3dDisplayMode.Format;//d3dDisplayMode.Format;//D3DFMT_A8R8G8B8;
d3dPresentationParameters.BackBufferCount = 1;
d3dPresentationParameters.BackBufferHeight = d3dDisplayMode.Height;
d3dPresentationParameters.BackBufferWidth = d3dDisplayMode.Width;
d3dPresentationParameters.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dPresentationParameters.MultiSampleQuality = 0;
d3dPresentationParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dPresentationParameters.hDeviceWindow = hwDesktop;
d3dPresentationParameters.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
d3dPresentationParameters.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;

if( d3d->CreateDevice(i,D3DDEVTYPE_HAL,hwDesktop,D3DCREATE_SOFTWARE_VERTEXPROCESSING,&d3dPresentationParameters,&d3dcdev) != D3D_OK )
    exit(1);

if( d3dcdev->CreateOffscreenPlainSurface(d3dDisplayMode.Width,d3dDisplayMode.Height,D3DFMT_A8R8G8B8,D3DPOOL_SYSTEMMEM,&sfScrn, NULL) != D3D_OK )
    exit(1);

if( d3dcdev->GetFrontBufferData(0,sfScrn) != D3D_OK)
    exit(1);   

// we now have a screenshot in sfScrn. 
// let's render it to a separate device in our app window!
d3dPresentationParameters.hDeviceWindow = hwDrawArea;
if( d3d->CreateDevice(i,D3DDEVTYPE_HAL,hwDrawArea,D3DCREATE_SOFTWARE_VERTEXPROCESSING,&d3dPresentationParameters,&d3drdev) != D3D_OK )
    exit(1);
d3drdev->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO,&sfBackBuffer);
if( d3drdev->CreateOffscreenPlainSurface(d3dDisplayMode.Width,d3dDisplayMode.Height,D3DFMT_A8R8G8B8,D3DPOOL_SYSTEMMEM,&sfTransfer, NULL) != D3D_OK )
    exit(1);

D3DLOCKED_RECT lockedRectScrn, lockedRectTransfer;
ZeroMemory(&lockedRectScrn, sizeof(D3DLOCKED_RECT));
ZeroMemory(&lockedRectTransfer, sizeof(D3DLOCKED_RECT));
if(sfScrn->LockRect(&lockedRectScrn,NULL,D3DLOCK_READONLY) != D3D_OK 
    || sfTransfer->LockRect(&lockedRectTransfer,NULL,0) != D3D_OK )
    exit(1);
memcpy((BYTE*)lockedRectTransfer.pBits,(BYTE*)lockedRectScrn.pBits,lockedRectScrn.Pitch*d3dDisplayMode.Height);
sfScrn->UnlockRect();
sfTransfer->UnlockRect();

while(true)
{
    if(d3drdev != NULL)
    {
        d3drdev->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(75,0,0),1.0f,0);
        if(D3D_OK!=d3drdev->StretchRect(sfTransfer,NULL,sfBackBuffer,NULL,D3DTEXF_NONE))
        {
            MessageBox(NULL,"failed to use stretchrect","",0);
            exit(1);
        }
        if(d3drdev->BeginScene())
        {
            d3drdev->EndScene();
        }
        d3drdev->Present(NULL,NULL,NULL,NULL);
    }
}

Edit2:

Oh! So apparently you can't use StretchRect() on surfaces that are in D3DPOOL_SYSTEMMEM, but you must use GetFronBufferData() on D3DPOOL_SYSTEMMEM.

user980058
  • 511
  • 7
  • 18

1 Answers1

2

Yes. you can capture the Windows desktop using GetFrontBufferData. As proof I can offer this recent question where the poster had it working, except not when the desktop was using 16-bit colour. It might give you some insight in how to use it correctly.

But no, that's the not the "true purpose" of GetFrontBufferData. It's real purpose is to allow Direct3D games to capture screenshots of the game itself regardless of whether the game is windowed or fullscreen, and most importantly, whether or not the game is using multisampling or not.

GetFrontBufferData isn't designed to be a better method to take screenshots of the Windows desktop. It might happen to be faster than other methods, but that's not why it exists.

Community
  • 1
  • 1
Ross Ridge
  • 38,414
  • 7
  • 81
  • 112
  • Hey, thanks! I realized my ability to get the screenshot into my other backbuffer is my main problem. Makes me wonder how many successful attempts I had at screenshotting before without realizing it. – user980058 Sep 27 '14 at 01:26