GetRenderTargetData succeeds, but only copies first pixel

Started by
3 comments, last by GameDev.net 17 years, 7 months ago
Hi all, I'm converting old DX8 code to DX9, and I need to grab the current backbuffer data and copy it onto a texture. This texture I then map onto a quad for rendering elsewhere. I accomplished this in DX8 by using IDirect3DDevice8::CopyRects(..). Worked fine and dandy-like. Trouble is, under DX9 that function no longer exists. It seemed that GetRenderTargetData would be useful for what I'm trying to do. Trouble is .. it succeeds (returns S_OK), but when I inspect the pixel data only the first pixel is modified. Here's a quick rundown of what I'm doing (see code below): (1) Grab my back-buffer description, and create a new texture with the pixel format, dimensions, Usage = 0, and pool = D3DPOOL_SYSTEMMEM. (2) Create the vertex buffers / shaders / etc for the quad-rendering (3) When I need to store the backbuffer data, I grab the backbuffer again (IDirect3DDevice9::GetBackBuffer(..)), get the target texture's 0th surface level (IDirect3DTexture9::GetSurfaceLevel(..)), and then call IDirect3DDevice9::GetRenderTargetData(..), passing in the backbuffer and the target texture. I have verified that the Quad/Texture rendering is working correctly by having it render another random texture, so that is ruled out. Now, GetRenderTargetData(..) returns S_OK, but I've placed in some code to inspect the data before and after, and only the first pixel is modified (pixel format is A8R8G8B8) Anybody have any ideas? Any help is greatly appreciated.. Graf


LPDIRECT3DTEXTURE9 g_pTexture = NULL;

void CMyClass::PrepareRenderBackBuffer()
{
	LPDIRECT3DSURFACE9	pBackBuffer;
	D3DSURFACE_DESC		desc;

	g_pd3dDevice->GetBackBuffer (0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
	pBackBuffer->GetDesc (&desc);
	pBackBuffer->Release ();

	width = desc.Width;
	height = desc.Height;

	if( FAILED (g_pd3dDevice->CreateTexture (desc.Width, desc.Height, 1, 0, desc.Format, D3DPOOL_SYSTEMMEM, &pTexture, NULL)))
	{
		cout << "CMyClass::PrepareRenderBackBuffer() : Failed to create texture!" << endl;
		return;
	}

	// .. lots of code to create a vertex buffer, shader, etc.

	BackBufferToTexture();
}

void	CMyClass::BackBufferToTexture()
{
	LPDIRECT3DSURFACE9		pBackBuffer;
	LPDIRECT3DSURFACE9		pTextureSurface;

	if (g_pTexture)
	{
		g_pd3dDevice->GetBackBuffer (0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);

		if (pBackBuffer)
		{
			g_pTexture->GetSurfaceLevel (0, &pTextureSurface);

			if (pTextureSurface)
			{	
				HRESULT res = g_pd3dDevice->GetRenderTargetData(pBackBuffer, pTextureSurface);
				if(FAILED(res))
				{
					switch(res)
					{
					case D3DERR_DRIVERINTERNALERROR:	cout << "D3DERR_DRIVERINTERNALERROR" << endl; break;
					case D3DERR_DEVICELOST:				cout << "D3DERR_DEVICELOST" << endl; break;
					case D3DERR_INVALIDCALL:			cout << "D3DERR_INVALIDCALL" << endl; break;
					}
				}

				pTextureSurface->Release();
			}

			pBackBuffer->Release ();
		}
	}
}


Advertisement
I can't tell why your code isn't working, but I can say that isn't the way I capture the pixels. This is how I do it:

void CaptureRenderTarget(IDirect3DDevice9* pdev, const TCHAR* path){	IDirect3DSurface9* pTargetSurface=NULL;	HRESULT hr=pdev->GetRenderTarget(0,&pTargetSurface);	if(SUCCEEDED(hr))	{		D3DSURFACE_DESC desc;		hr=pTargetSurface->GetDesc(&desc);		if(SUCCEEDED(hr))		{			IDirect3DTexture9* pTempTexture=NULL;			hr=pdev->CreateTexture(desc.Width,desc.Height,1,0,desc.Format,D3DPOOL_SYSTEMMEM,&pTempTexture,NULL);			if(SUCCEEDED(hr))			{				IDirect3DSurface9* pTempSurface=NULL;				hr=pTempTexture->GetSurfaceLevel(0,&pTempSurface);				if(SUCCEEDED(hr))				{					hr=pdev->GetRenderTargetData(pTargetSurface,pTempSurface);					if(SUCCEEDED(hr))					{						D3DXSaveTextureToFile(path,D3DXIFF_PNG,pTempTexture,NULL);					}					pTempSurface->Release();				}				pTempTexture->Release();			}		}		pTargetSurface->Release();	}}


Of course, you don't need to save the texture, you can do with it what ever you want.
.
I can't really tell you what's wrong, but first of all I'd suggest running with the debug runtime and seeing if any comments pop up. Regarding what Mastaba said, good things to try there are using GetRenderTarget instead of GetBackBuffer, and using D3DXSaveTextureToFile for testing. The really bad thing is allocating a texture during the process, which you're doing, too. Allocate that texture when you create the device, not each capture!

BTW, perhaps it's a 'formatting for posting' typo, but shouldn't the texture in the CreateTexture line be 'g_pTexture' and not 'pTexture'?
Hi Guys,

Thanks for the help =) I tried what Mastaba did, saving to file, and the texture looked fine.

Turns out that I couldn't render a texture in the SYSTEMMEM pool (anyone know why?). D3DPOOL_DEFAULT worked fine.

So what I ended up doing is taking Mastaba's approach, creating a temp texture in SYSTEMMEM, and then after the GetRenderTargetData call making a call to UpdateTexture(..), updating my final texture (in the default pool).

Also, you are indeed correct ET3D :) I spent about 20 minutes cleaning up / formatting the code for "public" view :) Missed one :D
Alot of them Rendertarget type functions use default, don't forget what you see on screen is actually on your video card! You really don't want to be doing things in system memory (or as little as possible), if you can help it. Mastaba is saving his texture, so he does need it in sytemmem. You could look into StretchRect, this does have a lot of limits to its use though, but I notice that both the source and dest are in default pool, this might mean it can do it without the texture having to come off to sytemmem and then back to default mem? - dunno really?

This topic is closed to new replies.

Advertisement