Hey,
i need to grab the fullscreen/imagebits/frame from a game into a char array. So I inject my grabberDLL via Detours in the d3d.dll into the "endScene".
I read that accessing the backbuffer is the fastes way doing it. So let me tell how I'm doing it:
unsigned char *bits;
IDirect3DSurface9 *pRenderTarget = NULL;
IDirect3DSurface9 *pDestTarget = NULL;
D3DSURFACE_DESC rtDesc;
D3DLOCKED_RECT rect;
void dumb_buffer(LPDIRECT3DDEVICE9 pDevice){
pDevice->GetRenderTarget(0,&pRenderTarget);
pRenderTarget->GetDesc(&rtDesc);
pDevice->SetRenderTarget(0, pRenderTarget);
pDevice->CreateOffscreenPlainSurface(rtDesc.Width, rtDesc.Height,rtDesc.Format, D3DPOOL_SYSTEMMEM, &pDestTarget, NULL);
pDevice->GetRenderTargetData(pRenderTarget,pDestTarget);
if(pDestTarget != NULL){
pDestTarget->LockRect(&rect,0, D3DLOCK_READONLY);
bits = (unsigned char*)rect.pBits;
pDestTarget->UnlockRect();
pDestTarget->Release();
}
pRenderTarget->Release();
}
This works, but it is very slow. As soon as I start this method the game stutters. If i change
pDevice->CreateOffscreenPlainSurface(rtDesc.Width, rtDesc.Height,rtDesc.Format, D3DPOOL_SYSTEMMEM, &pDestTarget, NULL);
to
pDevice->CreateOffscreenPlainSurface(rtDesc.Width, rtDesc.Height,rtDesc.Format, D3DPOOL_DEFAULT, &pDestTarget, NULL);
it works a littlebit faster, but then i get a black image. "bits" is then 0.
Another way is this variation: Instead of using this:
pDevice->CreateOffscreenPlainSurface(rtDesc.Width, rtDesc.Height,rtDesc.Format, D3DPOOL_SYSTEMMEM, &pDestTarget, NULL);
pDevice->GetRenderTargetData(pRenderTarget,pDestTarget);
if(pDestTarget != NULL){
pDestTarget->LockRect(&rect,0, D3DLOCK_READONLY);
bits = (unsigned char*)rect.pBits;
pDestTarget->UnlockRect();
}
i could also use a dynamic texture:
pDevice->CreateTexture(rtDesc.Width, rtDesc.Height,1, D3DUSAGE_DYNAMIC,rtDesc.Format,D3DPOOL_DEFAULT,&texture, NULL);
texture->LockRect(0,&rect, NULL, D3DLOCK_DISCARD);
bits = (unsigned char*)rect.pBits;
texture->UnlockRect(0);
but this does not help. The game still stutters.
Someone suggested to use "getBackBuffer", so the code would look like this:
IDirect3DSurface9* pSrcSurface;
IDirect3DSurface9* pTempSurface;
IDirect3DSurface9* pDestSurface;
D3DSURFACE_DESC pDesc;
void dumb_buffer(LPDIRECT3DDEVICE9 pDevice){
if(FAILED(pDevice->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO,&pSrcSurface)))
return;
pSrcSurface->GetDesc(&pDesc);
if(FAILED(pDevice->CreateRenderTarget(pDesc.Width,pDesc.Height,pDesc.Format,D3DMULTISAMPLE_NONE,0,FALSE,&pTempSurface,NULL)))
return;
if(FAILED(pDevice->CreateOffscreenPlainSurface(pDesc.Width,pDesc.Height,pDesc.Format,D3DPOOL_SYSTEMMEM,&pDestSurface,NULL)))
return;
if(FAILED(pDevice->StretchRect(pSrcSurface,NULL,pTempSurface,NULL,D3DTEXF_NONE )))
return;
if(FAILED(pDevice->GetRenderTargetData(pTempSurface,pDestSurface)))
return;
pDestSurface->LockRect(&rect,0, D3DLOCK_READONLY);
bits = (unsigned char*)rect.pBits;
pDestSurface->UnlockRect();
pDestSurface->Release();
pTempSurface->Release();
pSrcSurface->Release();
}
This does not work for me. I get a black image. "bits" is 0.
In all of this methods i'm using "LockRect(..)". When i comment these three lines:
//pDestSurface->LockRect(&rect,0, D3DLOCK_READONLY);
//bits = (unsigned char*)rect.pBits;
//pDestSurface->UnlockRect();
the game does not stutter, but of course then I get no image.
I wanted to mention that in the code snippet above I always create a new Surface/Texture each frame. At the end of my methode I release then the Surface/Texture. I also tried to initialize the Surface/Textture only once, so that I can use all the time the same Surface/Texture so I dont have to create a new one each frame. This does not help with the performance issue. There is no significant improvement with the frames.
Maybe the problem is that with using "LockRect(..)" I'm blocking my graphic card so the game starts to stutter/freeze for a very short time.
The only thing I want to do is to grab each frame of the game. I do not want to change anything. The problem is that the game has no API to grab the frame and stream it. So i have to do it via injection.
Is there a more efficent way to accomplish this? Maybe with another injection or another method? Maybe using pipelines or queues. I'm not an DirectX expert so any help would be great.
Is there a way to grab the frame without using "LockRect(..)"
I have a 64bit i7 @3,60GhZ 8 core pc with 16GB RAM and a NVIDIA Geforce GTX 670 with 2GB RAM. The Game is VBS2 2.02 with the Engine of the game ArmA II. So my PC must be powerfull enough to handle such a old game.
Thanks!
Regards
Denis
PS: I also tried to grab the Screen/Desktop and also useses DirectShow and GDI, but this does not work when the game is in fullscreen.