Render to system memory

Hey, I’m looking for a way to render a frame directly to system memory. I want this because I need to access and manipulate the bits of a frame, but reading from the video memory is unreasonably slow. glReadPixels gets me exactly what I want, but I don’t know to set up everything else so that it doesn’t render on the video card.

The frame I want to render to system memory is simpler than the actual frame I want to render as output, so I also want to keep my current ability to do normal rendering. I’ve been working on this for a couple days now, looking around the web, but I haven’t really found anything useful yet. This forum looks most promising.

Also note that I’m doing this in Windows, with wgl stuff, in case that makes a difference.

If you’re rendering to system memory, you’ll either be ReadPixelling or using a software renderer. If the Windows software renderer won’t do it for you, I’m sure Mesa would work.

If you use any kind of texturing, it’s likely that rendering to framebuffer and using ReadPixels will be faster than rendering to system memory (which has to use the MS software renderer).

If you need lower resolution, you can glScissor() and set up a smaller projection matrix to save on clear or fill rate.

Last, ReadPixels is very dependent on what format you use to read. I suggest using a framebuffer of 24 bits color and 8 bit alpha, and reading back GL_BGRA/GL_UNSIGNED_BYTE format pixels.

I’m not drawing any textures. In fact I’m turning off everything I can to just get a 2 color black and white image. I’m also drawing very few objects compared to the final render. I figured I’d be rendering without HW support if I wanted to render off the video card.

You could implement it both ways and profile. My guess is that, stalls and all, you’ll get better performance letting the graphics card rasterize than having the CPU do it, although it of course depends on specific parameters of the situation.

I don’t know how to implement both ways. I already have the program using the hardware rasterizer and memory. I want to use the software rasterizer and system memory. That’s what I don’t know how to do. That’s what I’m asking how to do.

All I want is a two-color (or 32bit RGBA), black and white, rasterized image, with 1/20th the number of objects, with no textures, no alpha blending, nothing fancy, in system memory that I can read from. Is that too much to ask for?

Originally posted by Monolith:
I want to use the software rasterizer and system memory.

Maybe I missed a trick, but can’t you render to a bitmap using PFD_DRAW_TO_BITMAP?

The method that works for me is
in Chapter 18 of OpenGL Super Bible
Second Edition, pp. 595-600.
You need to create a DIB, then
choose a pixel format with the
format descriptor dwFlags set to
PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL.
Remember to destroy the DIB later.

Software rasterization it is… http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnopen/html/msdn_gl6.asp

Originally posted by roffe:
[b]
Software rasterization it is… http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnopen/html/msdn_gl6 .asp

[/b]

That web page was for Windows NT 3.5. It’s much easier now. Here are code examples for Win2k/XP in straight win32 API.

Normally you would create an image called m_hBitmapImage as follows:

m_hBitmapImage = CreateCompatibleBitmap( hDc, m_cxImage, m_cyImage );

You would then be able to select m_hBitmapImage into a memory device context and draw on it with GDI commands. That image is called a DDB, or Device Dependent Bitmap.

To be able to use OpenGL commands on it, you
need to create it as a DIB instead of as a DDB. A DIB is a Device Independent Bitmap. Create the image as a DIB as follows:


hDc = GetDC( NULL );
memset( &m_BitmapInfo, 0, sizeof(BITMAPINFO) );
m_BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
m_BitmapInfo.bmiHeader.biWidth = m_cxImage;
m_BitmapInfo.bmiHeader.biHeight = m_cyImage;
m_BitmapInfo.bmiHeader.biPlanes = 1;
m_BitmapInfo.bmiHeader.biBitCount = 24;
m_BitmapInfo.bmiHeader.biCompression = BI_RGB;
m_hBitmapImage = CreateDIBSection( hDc, &m_BitmapInfo, DIB_RGB_COLORS,
(void **)(&m_pBitmapBits), NULL, 0 );
ReleaseDC( NULL, hDc );


Notice that we used a real device context, not a memory device context. Now we’re ready to draw on it with a memory device context. First create the memory device context and select the DIB into it:


hDc = CreateCompatibleDC( NULL );
hBitmapPrev = (HBITMAP)SelectObject( hDc, m_hBitmapImage );


Next I use the GDI Rectangle command to clear the image and give it a border:


Rectangle( hDc, 0, 0, m_cxImage, m_cyImage );


The GDI Rectangle command will use whatever pen and brush you selected into the device context. Select the desired pen and brush into hDc before calling Rectangle(), then unselect (and if necessary delete) the pen and brush.

Next, set up the pixel format. This code snippet shows important format descriptor settings:


pfd.dwFlags = PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cRedBits = 8;
pfd.cGreenBits = 8;
pfd.cBlueBits = 8;
pfd.cDepthBits = 16;


I don’t think Windows uses the Alpha value, but you still must use PFD_TYPE_RGBA to match Windows’ byte boundaries (they allocate 4 bytes per color).

Set pfd.cDepthBits to 32 if you want a 32 bit instead of 16 bit depth buffer. After using ChoosePixelFormat() and SetPixelFormat() you’re ready to render:


hRenderContext = wglCreateContext( hDc );
wglMakeCurrent( hDc, hRenderContext );

// OpenGL go commands here . . .

wglMakeCurrent( hDc, NULL );
wglDeleteContext( hRenderContext );

SelectObject( hDc, hBitmapPrev ); // Unselect the DIB from hDc
DeleteDC( hDc ); // Delete the Memory Device Context


Implement your OpenGL commands at the comment placeholder.

You can now use the DIB as a regular Windows bitmap.

By the way, all of the code above was in its own thread. That is because I typically have more than one OpenGL window going, and each current rendering context must be in its own thread. Theoretically, you should be able to implement that code in your main program thread. In that case, make sure you have no other rendering contexts current. If that doesn’t work, try not even defining any other rendering contexts.

Creating the thread for the code above is easy. It does not need a message loop (as a full blown OpenGL window requires), and it does not need synchronization, except to notify the parent window when it is done. That can be done by posting a WM_APP message to the parent window – be sure to use PostMessage(), not SendMessage(). When the parent window receives the message it terminates the thread, and you still have the DIB handle (m_hBitmapImage) because it was instantiated in a class in the main program thread. You can then use the DIB handle as a Windows bitmap in other threads.

For example, you can BitBlt it to the screen in a WM_PAINT handler as follows:


hdcMem = CreateCompatibleDC( hDc );
hBitmapPrev = (HBITMAP)SelectObject( hdcMem, m_hBitmapImage );
BitBlt( hDc, 0, 0, m_cxImage, m_cyImage,
hdcMem, 0, 0, SRCCOPY );
SelectObject( hdcMem, hBitmapPrev );
DeleteDC( hdcMem );


Finally, before your program ends, destroy the DIB:


DeleteObject( m_hBitmapImage );


This method of rendering offscreen works good for me. It’s pretty simple once you get the syntax correct. This posting should help others along that path.

I am writing apps for Win2k/XP with straight win32 API (no MFC). I have encountered some pitfalls, such as getting the syntax right for rendering offscreen, for which I have included the solution above. I have found other solutions and work-arounds, but I continually find new problems with developing for OpenGL on Windows.

My apps are not games, but I do include some game type features. Is this a lost art, to develop midrange apps for OpenGL on Windows? I hope programmers can help advance OpenGL on Windows by posting simple fixes and work-arounds like this. For example, I had problems with the mouse cursor when I don’t continually refresh the screen (in simple event-driven programming, which is important to use whenever possible to reduce resource usage and allow users to use other programs while yours is running). I fixed that by calling SetCursor( NULL ) just before rendering, then setting the previous cursor after rendering. I found this works best when the window’s class cursor is set to NULL, in which case you need to explicitly load and set the cursor when the window is created.

Another problem is using GDI on an OpenGL window after calling SwapBuffers(). Some Microsoft help pages say you can’t do that at all, and some of their help pages say you can, but not with double buffering. I found you can do it with double buffering, but that makes your app unstable in some circumstances. As such, I’ve decided not to use that. But I wonder if it is possible somehow.

Yet another problem I noticed recently was the occasional corruption of tooltips from toolbars over OpenGL windows in Windows XP (not on Win2k). I have not found a fix for that, but the problem seems benign enough to just let it happen.

Any help on Windows specific stuff like that will be appreciated. Help advance OpenGL on Windows by posting programming information and fixes on how to get OpenGL to work on Win2k/XP.

Thanks,

Carlos Portela

Originally posted by Carlos Portela:
That web page was for Windows NT 3.5. It’s much easier now. Here are code examples for Win2k/XP in straight win32 API.

To my knowledge the GDI api hasn’t changed when it concerns DIBs. But since you use them frequently I’m sure you know better.

Personally, if I was working on something that REQUIRED software rendering using opengl I would use MESA. Old gl1.1 drivers that hasn’t been updated since 95 just doesn’t do it for me.

I am trying to draw into a bitmap, as you wrote about before, but I get a “ERROR_INVALID_PIXEL_FORMAT” message when I call wglCreateContext() using the following code:

HDC hdc=GetDC(null);
BITMAPINFO m_BitmapInfo;
memset(&m_BitmapInfo,0,sizeof(BITMAPINFO));
m_BitmapInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
m_BitmapInfo.bmiHeader.biWidth=wid;
m_BitmapInfo.bmiHeader.biHeight=hgt;
m_BitmapInfo.bmiHeader.biPlanes=1;
m_BitmapInfo.bmiHeader.biBitCount=24;
m_BitmapInfo.bmiHeader.biCompression=BI_RGB;
sBitMap=CreateDIBSection(hdc,&m_BitmapInfo,DIB_RGB_COLORS,(void **)(&m_pBitmapBits),NULL,0);
ReleaseDC(null,hdc);
mDC=CreateCompatibleDC(null);
if ((mRC=wglCreateContext((HDC)mDC)) == null)
ErrorBox("",GetLastError());

and here is my pixel format:

static PIXELFORMATDESCRIPTOR pfd={
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW |
PFD_SUPPORT_OPENGL |
PFD_DRAW_TO_BITMAP |
PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
24,
0, 0, 0, 0, 0, 0,
0,
0,
0,
0, 0, 0, 0,
16,
0,
0,
PFD_MAIN_PLANE,
0,
0, 0, 0
};

any help would be most appreciated!

thanks!

bill

You have both DRAW_TO_WINDOW and DRAW_TO_BITMAP defined in the PFD

Thanks for your quick reply!

I tried with a new PFD with only PFD_DRAW_TO_BITMAP set, but now
the nvidia drive offers its own dialog error saying “Driver Components mis-match. Exit is the only option.”
after I call ChoosePixelFormat(), and the app exits…

any ideas?

thanks!

bill

I’m seeing this very error after reinstalling Windows 2K, also with the NVidia OpenGL driver, on unchanged sw that was working correctly before I tried to fix a Windows Explorer error (unsuccessfully). I’ve tried uninstalling OpenGL, installing DirectX, and reinstalling OpenGL, but the error (in a VisAd app) persists. Any ideas?

I spent hours trying to decrypt MS’ tech article on offscreen rendering. I’ve been working for a while now with OpenGL & Microsoft VC, but still haven’t been able to get the catch on all these Rendering contexts, device contexts, etc. Though I understand some of the stuff, I still can’t start from scratch or modify any of the rendering-to-window apps I’v already made in order to get this going, even after your posts.
Basically I’m not only trying to render to ram, but also writing to HDD, but once on RAM it should be fairly easy.
Could you please (please!) e-mail me a working W32 app that simply starts - renders to DIB - quits? Or posting the complete source code to create de DIB, pixelformatting and such? I’d be greatly appreciated.

J.P.Canepa
Santiago, Chile.