So, you’re writing a game, and you want to take a screenshot. The most common code sample you’ll probably find around the web will look something like this:
LPDIRECT3DSURFACE9 pd3dsBack = NULL;
LPDIRECT3DSURFACE9 pd3dsTemp = NULL;
if (SUCCEEDED(device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pd3dsBack)))
{
D3DSURFACE_DESC desc;
pd3dsBack->GetDesc(&desc);
if (SUCCEEDED(device->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &pd3dsTemp, NULL))
{
if (SUCCEEDED(device->GetRenderTargetData(pd3dsBack, pd3dsTemp)))
{
LPD3DXBUFFER pBuf;
D3DXSaveSurfaceToFileInMemory(&pBuf, D3DXIFF_BMP, t, NULL, NULL);
}
pd3dsTemp->Release();
}
pd3dsBack->Release();
}
Now what’s wrong with that? You’ll end up with a BMP in pBuf and then you can do stuff with it, like save it. Or you can use the more direct D3DXSaveSurfaceToFile to save direct to disk (although, handy hint, push the buffer to a thread and save it outside of the rendering loop… no pause in the renderer then.
This code only works with non-multisampled displays too. There is way around that, that’s outside the scope of this post. So… what’s wrong using D3DX to save a picture? Let’s consider these issues that I have come up against:
- It’s slow – very very slow. If you try and save a PNG it’ll take upwards of a second. JPG is faster!? BMP is fastest.
- It sometimes doesn’t even take the shot – you’ll just get a bunch of zeroes
Yep, something isn’t quite right there. I’m using the November 2008 SDK and I get a bunch of zeroes from the surface in certain circumstances. Why? Well that’s a very good question. I have no idea why. However, if I make use of LockRect and copy the right bits out that way I get a 100% success rate of reading back the BackBuffer.
So, the surface clearly always has the data, but D3DX fails to read it properly. Obviously, without the source code to D3DXSaveSurfaceToFileInMemory I can’t do any more research into the matter. Taksi has some interesting samples on the topic (although it’s geared to copying the pitch data as well and in fact converting pitch, which you won’t always need to do).

