Why can't I lock a RenderTexture in DirectX9?

In Unity I grab the Pointer to the RenderTexture with GetNativeTexturePtr() and send that to my plugin (like the native plugin example)

Only when I want to lock the Texture with the following code in my plugin I get an D3DERR_INVALIDCALL

HRESULT hr;
IDirect3DTexture9* d3dtex = (IDirect3DTexture9*)g_TexturePointer;
D3DSURFACE_DESC desc;
d3dtex->GetLevelDesc (0, &desc);
D3DLOCKED_RECT lr;
hr = d3dtex->LockRect (0, &lr, NULL, 0);
// hr is S_OK when i send a Texture2D but not when it's a RenderTexture howcome?

I’m guessing it has something to do with Memory location of the RenderTexture?

I want to read the pixels of the RenderTexture in Direct3D 9

2 Answers

2

After figuring out that render textures are stored in the Default D3D memory pool… I found out that I needed to access it’s data through a GetRenderTargetData method.
after retrieving the surface from the texture and using the aforementioned method it worked :slight_smile:

IDirect3DTexture9* d3dtex = (IDirect3DTexture9*)g_TexturePointer;
D3DSURFACE_DESC desc;
d3dtex->GetLevelDesc (0, &desc);

if(desc.Pool == D3DPOOL_DEFAULT)
{
	g_IsRenderTexture = true;
	IDirect3DSurface9* offscreenSurface;
	HRESULT hr = g_D3D9Device->CreateOffscreenPlainSurface( desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &offscreenSurface, NULL );
	if(hr == S_OK) 
	{
		g_SurfacePointer = offscreenSurface;
	}
	else
		print_to_log("Couldn't create Offscreen surface for RenderTexture");
	}
}

To copy the render texture you need to get it’s surface data:

// Get References
IDirect3DTexture9* d3dtex = (IDirect3DTexture9*)g_TexturePointer;
IDirect3DSurface9* d3dsurf = (IDirect3DSurface9*)g_SurfacePointer;
				
// Get Texture Surface
IDirect3DSurface9* tmpSurf;
d3dtex->GetSurfaceLevel(0,&tmpSurf);

// Copy Surface to Surface
g_D3D9Device->GetRenderTargetData(tmpSurf,d3dsurf);

Do you mind posting a quick snippet of your code as an example? I have had the same issue as you and am trying to access a Unity RT via the GetRenderTargetData method though I always get D3DERR_INVALIDCALL in return. I am creating the destination surface using CreateOffscreenPlainSurface.

hows that?

Thanks! Actually my problem seems to lie in the GetRenderTargetData call. I can create the offscreen surface to hold the data retrieved without throwing errors but it is the GetRenderTargetData call itself that is failing. I am assuming that you are creating your surface as shown above as the second parameter on the GetRenderTargetData call correct? How are you preparing the first parameter? I think that is where my error lies.

I get where you're coming from.. the Texture is a Texture and not a Surface and GetRenderTargetData wants a surface so you need to get the surface from the texture with GetSurfaceLevel().. you follow? added code to answer d3dsurf is the offscreenplain :) plz up vote if it works :)

I've edited my above comment to include a cut down version of my current attempt. Edit: I think I know where the problem lies. I enabled D3D9 debug and inspected the texture being passed in, it is not valid (though the GetLevelDesc works fine?). I suspect my problem lies in the C# code where the RenderTexture is sent up to the dll.

I actually ran into a similar issue recently on a colleague’s machine - he was running very old drivers and after updating it fixed the issue.

Did you manage to get it working on your end?

Yeah, the above code worked fine. It ended up being the d3d9 device pointer pointing to a device created within the dll rather than the Unity d3d9 device.