PDA

View Full Version : Blitting SDL + OpenGL screen surface to another surface?


Sharkbait
07-29-2005, 12:02 AM
My 'game engine' supports both software rendering through plain SDL software surfaces and hardware rendering via OpenGL over SDL. In keeping with the capabilities of the software mode, I would like to have the ability to treat the screen (that is, the OpenGL frame or backbuffer) as a blittable surface in itself that I can blit over any other surface, particularly non-screen onse.

I tried various approaches, namely reading off the pixel data from the SDL surface and using glReadPixels, but I am getting very weird behaviour - although the pixel format of the screen SDL surface/OpenGL framebuffer appears normal (32 bits/pixel, RGBA), it still behaves freakishly. For example, even if I try to fill the whole buffer with 0xFF (to get an opaque white result), I still get vertical coloured columns. Any idea or pointers to what I might be missing here?

Thanks!

[edit] fixed stupid typos

mahlzeit
07-29-2005, 02:25 AM
SDL has something (apparently evil) called OPENGLBLIT, although I don't know if that will do what you wish.

Sharkbait
07-29-2005, 02:38 AM
Yes, that's one of two possible flags that need to be set to enable OpenGL over SDL. The SDL_OPENGLBLIT flag allows both OpenGL and 'native' SDL blitting (provided you call SDL_UpdateRects(...) after the latter to synchronise with OpenGL blits). The other alternative is SDL_OPENGL, but this simply allows OpenGL blits with no native SDL blits. In my case it doesn't really matter which flag I use since I implement blits to the screen surface using a textured quad. My problem is blitting the other way round, that is, from the screen on to some other (offscreen) surface.

In case you're wondering why I need this specific functionality, I use it for screen fade outs when I am switching from one game state to another, such as from the main menu to the highscore screen. On switching to the highscore screen, I grab the screen contents (still containing the last menu screen render) on to a surface that I then fade out gradually on the second screen for the first 1/2 a second or so of the highscore screen state, effectively implementing a cross-fade from one screen to another.

Pending a failure to find a way of blitting screen contents on to a surface (with SDL/OpenGL), my other alternative is to force a second render on to a n off-screen surface (much like OpenGL render to texture), but that would obviously be more complicated especially since I have to do it in all my game states.

soniCron
07-29-2005, 08:55 AM
I just wanted to point everyone to an old post about SDL_OPENGLBLIT that I think is very imporant. Read it here. (http://twomix.devolution.com/pipermail/sdl/2001-November/039852.html)

Sharkbait
07-30-2005, 08:13 AM
I think that guy made his point clearly enough! :)

Actually, it doesn't really matter if I use OPENGL or OPENGLBLIT since I never call SDL blitting functions when rendering to the screen/backbuffer anyway. I do however keep the SDL software surfaces to restore the corresponding textures on fullscreen toggling and offscreen-to-offscreen renders.

My last piece in the puzzle is how to treat screen-to-offscreen blits.

For the record, I am using the non-evil OPENGL flag only :)

stan
07-30-2005, 02:31 PM
Err... In Smart Lines, I'm grabbing the contents of my opengl screen and creating a texture of it, when a dialog box is opened.

Here's the glReadPixels() part:


GLuint Texture_MakeBigPicFromGLBuffer( GLenum buffer, int x, int y, int width, int height )
{
int line;
GLint viewport[4];
char *pix = NULL;
GLuint bgpic = 0;
int wid = Display_GetWidth();
int hei = Display_GetHeight();
int width2 = width;
int height2 = height;

ASSERT( buffer == GL_FRONT || buffer == GL_BACK );

glGetIntegerv( GL_VIEWPORT, viewport );
ASSERT( wid == viewport[2] );
ASSERT( hei == viewport[3] );

glReadBuffer( buffer );
pix = malloc( width2 * ( height2 + 1 ) * 3 ); // height+1 because an extra line is needed for the invertion
if( pix )
{
glPixelStorei( GL_PACK_ALIGNMENT, 1 );
glPixelStorei( GL_PACK_ROW_LENGTH, width ); /* wid or width ? */
glReadPixels( x, hei-y-height, width, height, GL_RGB, GL_UNSIGNED_BYTE, pix );
int e = glGetError();
if( e ) { PF( "### glerror: %u\n", e ); }
else
{
// stupid glReadPixels() reads from bottom to top, so we have to
// invert the data
for( line = 0; line < height/2; line++ )
{
memcpy( pix + 3 * ( height * width ), pix + 3 * line * width, width * 3 );
memcpy( pix + 3 * line * width, pix + 3 * ( height - 1 - line ) * width, width * 3 );
memcpy( pix + 3 * ( height - 1 - line ) * width, pix + 3 * ( height * width ), width * 3 );
}
bgpic = Texture_MakeBigPic( pix, width2, height2, GL_RGB );
}
free( pix );
}

return bgpic;
}


This could be simplified but I don't want to introduce a bug while doing that :).

Now the texture creation part... My Texture_MakeBigPic() function is a bit long, so I'll try to list the relevant parts:

glPixelStorei( GL_UNPACK_ROW_LENGTH, width );
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
glBindTexture( GL_TEXTURE_2D, glname );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, texw, texh, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer );

Well, I don't think I'm doing much more magical things. Hopefully you'll see something that you've missed... Unless, of course, Smart Lines also displays vertical stripes on your system :).

Sharkbait
08-01-2005, 07:53 AM
stan,

Thanks for the handly snippet! :) The main difference I see is that you are specifying byte alignment and row length, something that I am not doing in my code. Judging from the 'vertical lines' behaviour, I suspect it is indeed an alignment setting. I will work on it asap.

Regards

Sharkbait
08-04-2005, 01:47 AM
Ok.. one fried and eventually replaced wireless router later and several family commitments out of the way, I finally got down to working on the problem.

Something very odd is happening. I am still working on the hardware rendering code to complement the software rendering and some of the fancy stuff is still being done in software (I cloned the software code and I am progressively changing it). I would expect the software renders not to show up since it is not being done by OpenGL, software renders go to a temporary SDL software surface, and since I am using the OPENGL (and not OPENGLBLIT) flag.

Now here's the odd part: The glReadPixels(..) code is picking up only the software rendered elements! If anything, I would expect the code to pick up the OpenGL rendered elements only, not the software stuff!

Colour me confused...

Sharkbait
08-12-2005, 03:20 PM
Apologies for raising a somewhat stale thread back from the dead, and also for bumping it, but I felt a post detailing the solution was in order.

The vertical columns issue turned out to be due to me picking up data in RGB format with glReadPixels and sticking it in an RGBA SDL buffer. The problem was corrected using glRGBA as a picking format.

My next problem was that the grabbed pixel data was blank. I eventually traced the problem down to my buffer flipping code that was issuing a glClear to clear the back buffer right after flipping. No wonder I was getting blank data!

Thanks to Stan et al for the help.