PDA

View Full Version : Accessing another Window's frame buffer



JohnTheSoftware
05-29-2012, 07:40 AM
Hi,

I'm hoping to read the frame buffer of a VBS2 window via the VBS2 supplied plugin functionality. Code as follows:-

HWND vbs2_hWnd = NULL;
RECT rect;
vbs2_hWnd = FindWindow(NULL,L"VBS2");
GetWindowRect(vbs2_hWnd,&rect);

HDC hdc=GetDC(vbs2_hWnd);

PIXELFORMATDESCRIPTOR pfd;


ZeroMemory( &pfd, sizeof( pfd ) );
pfd.nSize = sizeof( pfd );
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cDepthBits = 32;
pfd.iLayerType = PFD_MAIN_PLANE;


int format = ChoosePixelFormat( hdc, &pfd );
bool pixelFormatSet = SetPixelFormat( hdc, format, &pfd );
HGLRC mhRC = wglCreateContext( hdc );

bool madeCurrent = wglMakeCurrent( hdc, mhRC );
GLenum err = glGetError();
if (err!=0)
printf("wglMakeCurrent error = %d\n",err);

DWORD dwBmpSize = (rect.right-rect.left)*(rect.bottom-rect.top)*4;

lpbitmap = (char*)malloc(dwBmpSize);

glReadBuffer(GL_BACK);
err = glGetError();
if (err!=0)
printf("glReadBuffer error = %d\n",err);

err = glewInit();
if (err!=0)
printf("glewInit error = %d\n",err);
GLuint pboBuffer;
glGenBuffers(1,&pboBuffer);
glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB,pboBuffer);
glBufferData(GL_PIXEL_PACK_BUFFER_ARB,dwBmpSize,0, GL_STREAM_READ);

err = glGetError();
if (err!=0)
printf("SwapBuffers error = %d\n",err);

glReadPixels(0,0,rect.right-rect.left,rect.bottom-rect.top,GL_RGBA,GL_UNSIGNED_BYTE,0);
err = glGetError();
if (err!=0)
printf("glReadPixel error = %d\n",err);
memcpy(lpbitmap,glMapBuffer(GL_PIXEL_PACK_BUFFER_A RB,GL_READ_ONLY_ARB),dwBmpSize);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB);


free(lpbitmap);
wglMakeCurrent(NULL,NULL);
wglDeleteContext(mhRC);

Everything seems to work fine except there's no data (00 00 00 FF per pixel). I know the context's correct because if I swap buffers it mucks things up in precisely the way one might expect.

Is it actually possible to read another Windows image data in this way? I can imagine that writing might be contentious.

I've already done it using BitBlt etc. and this works fine, but I had hoped to get the depth data for some laser range simulation and make it run a bit quicker.

Thanks

John

tonyo_au
05-29-2012, 06:51 PM
What is VBS2? Are its windows' content created via OpenGL?

JohnTheSoftware
05-29-2012, 11:36 PM
What is VBS2? Are its windows' content created via OpenGL?

Hi Tonyo,

VBS2 is a commercial distributed multi-user battle simulation gaming product and the answer to the second question is 'I don't know'. VBS2 is Windows only, but I had assumed that as the underlying hardware is identical whichever driver is used (I guess the other option is DirectX) then either method would do. Is this not the case? I suppose it could depend on where the concept of, for example, a framebuffer, exists. If this is a software construct in OpenGL that has no analogue in DirectX then I can see that I might get nowhere. Or I am I talking bollocks. I'll try and do the same thing in DirectX (which I have never used) :(.


John

tonyo_au
05-30-2012, 02:22 AM
I don't know if you can use OpenGl to grab any generic window. I would not think it would work but as a minimum you need to change glReadBuffer(GL_BACK); to glReadBuffer(GL_FRONT); becuase the only buffer you know actually exists is the window
currently displayed on the screen. glReadBuffer(GL_BACK) wants to read from the back buffer in a double buffer system and it will be different in OpenGL and DirectX and you don't know if they even use one.

I know OpenGL and DirectX buffers differ slightly because there is a specific call in OpenGL to bind to a DirectX buffer. A buffer is created with specific information about it's format like whether is has a alpha field, are the colour components (RGB) 8 bit, 32 bit, integer or float, does it have a depth buffer and a stencil buffer. As you can might expect information is held differently in OpenGL to DirectX. And of couse DirectX has several different formats (DirectX 9, DirectX10 etc).

If you want to grab the frame, and the plug-in doesn't provide a function to do this, I would look at some Windows screen capture code. Basically you create a device compatible bitmap and blit to this bitmap. Now you can grab the bits and save them as you like. These are all Windows function calls. Be warned though if a window overlaps the one you are capturing you will get part of that window in the captured frame.

V-man
05-30-2012, 04:41 AM
No, you can't read the GL surface of another process. If the plugin runs in the same process as the VBS2, then you should be able to do it (just like it is possible to do it in Blender). In Blender, we just call gl functions. There is no need to create a gl context because it uses Blender's GL context. You seem to be creating a GL context and that obviously isn't going to work.

If the program is using Direct3D, then you would have to ask on the Direct3D forums.

JohnTheSoftware
05-30-2012, 09:40 AM
Thanks guys. I think that pretty much wraps it up. The VBS2 plugin functionality is a dll with a defined interface with a procedure that's called every frame. I imagine that constitutes being part of the same process. I'm pretty sure I tried using the OpenGL calls without defining a context and that just failed. I'll try the DirectX stuff and see if I get a result. If I get anywhere using OpenGL I'll report back.

Thanks again

John