Problems copying screen with glCopyTexImage2D

I’m working on a 2D game and am using OpenGL to draw the screen from sprites converted into textures. Everything works fine, except that it takes quite a lot of time to draw the entire screen every refresh as I’m drawing a bunch of textured quads (I’m mimicking a 80x50 console output, so am drawing 4000 quads - one for each “character” on the “console”). I want to employ dirty rectangles and only redraw the parts of the screen that have changed. I figured I could just use glCopyTexImage2D to copy the screen into a few power of 2 textures, but I just can’t get it to work. Here’s a code snippet:

// Getting the pixels from the upper left 512x512 part of the screen:
glBindTexture(GL_TEXTURE_2D, ScreenTextureID[1, 1]);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 512, 512, 0);

// Clear the screen:

// Drawing the saved screen texture back to the screen:
glBindTexture(GL_TEXTURE_2D, ScreenTextureID[1, 1]);
glTexCoord2f(0, 0);
glVertex3f(0, 0, 0.0);
glTexCoord2f(1, 0);
glVertex3f(512, 0, 0);
glTexCoord2f(1, 1);
glVertex3f(512, 512, 0);
glTexCoord2f(0, 1);
glVertex3f(0, 512, 0);

This just draws a flickering image with some parts of the correct image mirrored and drawn twice. I’ve tried drawing another image and that works fine, so the error seems to be in the part of my code that saves the screen pixel information into the texture, not in the part that draws it back.

why switch to the front buffer? This will usually contain the previous frames contents and not what you have drawn this frame (which is in the back buffer). I don’t know what you have setup (double buffering) or perhaps this is what you are trying to acheive?

For performance you could use glCopyTexSubImage2D as this avoids converting data formats for each texture upload.

Finally, you have not shown that you have set a suitable projection matrix and setup the viewport with the correct w and h. If you don’t do this you may get garbage in your textures.

If you’re double buffering, you’re most likely going to want to be reading the back buffer (GL_BACK) if you’re calling this before SwapBuffers. Also be aware that glCopyTexImage2D allocates the MIP and copies pixels, while glCopyTexSubImage2D only copies the pixels. For best perf, you probably want the latter. To preallocate the texture MIPs, call glTexImage2D with a NULL pointer for the data.

I have set up the projection matrix and everything else in the initialization of my program. Like I said, everything else works perfectly and draws to the screen like it’s supposed to - I’m just having trouble with saving the contents of the screen and redrawing it for use with my dirty rectangles approach.

What I want to do is save the contents of the previous frame (i.e. what’s currently on the screen) before I clear it to draw the next frame. I only want to refresh the parts that have changed, but that means I have to save the parts of the screen that haven’t changed and redraw them. That’s why I’m saving the contents of the screen and drawing it to the back buffer before before drawing the changes to the new frame to the back buffer.

I tried switching to glCopyTexSubImage2D, which gave a bit of progress. Instead of a garbled image I’m now getting a white box on the 512x512 upper left part of the screen that I’m saving and redrawing.

EDIT: I was passing incorrect parameters when switching to glCopyTexSubImage2D. With the correct parameters I’m getting the same result: garbled image.

EDIT 2: Here’s an image of the resulting screen.

And what it looks like when I remove the dirty rectangles stuff I’m trying to do and just render all 4000 quads:

Can you be clearer with what you are doing?
Are you either:
a) Copying the entire screen (back buffer) to a texture, or
b) Copying various portions of the screen to various textures?

I suspect you mean a) - copy the entire screen, in which case this should be very straight forward.
Directly after you have drawn the scene to the back buffer, simply bind your texture and call glCopyTexSubImage2D (GL_TEXTURE2D,0,0,0,0,0, Tex_w, Tex_h). No need to switch to front buffer or anything like that. For this to work, the texture size must match the screen size (viewport dimensions) otherwise you are going to get strange results. If your screen is set to the main GL rendering context (system default window) then that is usually some non-power of two dimension, eg 1024 * 768. Therefore you need to ensure you can support NPOT textures (OpenGL 2.0 or EXT_Non_Power_ofTwo) and you have sized the textures accordingly.
I have a feeling you are trying to capture the ‘screen’ into a small texture (512 * 512) but have not resized the viewport to the texture dimensions.

“For this to work, the texture size must match the screen size”
not really essential. important is that the texture (if NPOT not supported) is bigger then the backbuffer. you will need to adjust texture coordinates < 1.0f in ST directions to draw it correctly. recommend NPOT textures though to not waste any vram.

I’m not saving the entire screen into a power of two texture. I’m trying to save parts of the screen into multiple textures. But right now all I’m trying to do is save the (0,0,512,512) part of the screen into a 512x512 texture. If I can get that to work, it’ll just be a matter of copying that code for the remaining screen.

Here’s what I want to do in my “draw the screen” procedure:

  1. Save the contents on the screen (the last frame) to textures.
  2. Clear the screen.
  3. Redraw the screen saved in 1).
  4. Draw the parts of the screen that have changed since the last frame.

Like I said, I’m emulating a 80x50 console and don’t want to draw 4000 quads every refresh. So saving the last frame and redrawing it, and then just redrawing the quads that have changed will save a lot of time, as I will only have to draw the 4 or so 512x512 textures that make up the last frame and then the ~100 quads that have changed.

I’ve used SDL for drawing the screen in the past, and it wasn’t a problem at all to use dirty rectangles with that, as the screen is just a surface (texture) like any other, so you could just draw the changes to that surface and blit it to the screen. With double buffered OpenGL, I lose the contents of my screen “surface” when swapping buffers, so I can’t just draw the changed quads, as everything else would be lost.

Use glCopyTexSubImage2D() just before your glSwapBuffers() in the previous frame and use the copied texture in the current frame as background.
Usually the content of the “non-rendering” buffer will become undefined (implementation specific) after the swap.

If this “draw the screen” procedure is really a single procedure/function call then how are steps 1-3 any different that just step 1 ? I.e. you draw to the screen, clear it and then redraw exactly what you had before.

For what you are trying to achive (take a copy of the back buffer) you need to drop step 1 from the draw procedure and perform the copy at the end of the render loop (or just before the swap buffers - same thing really). The point here is you can’t do it before the render loop has begun because you’ll have lost the previous frame’s contents during the swap buffers process.

I tried that and it gives the exact same result.

I’m not trying to save the back buffer. I’m trying to save the front buffer and copy it to the back buffer. The front buffer is what’s currently on the screen, right?

So what I’m trying to do is:

  1. Save the front buffer.
  2. Copy it to the back buffer.
  3. Draw all the quads that have changed to the back buffer.
  4. Swap buffer, so what will be drawn is the last frame along with all the stuff that has changed for the current frame.

Unless you have done some setup that’s different to the norm, then this not what usually happens.
GL implementations actually don’t like the application drawing to the front buffer directly as it messes up all sorts of driver optimisations. Instead, application draw to the back buffer. During the swapbuffers operaton, the back becomes the front and that is blitted to the screen. Therefore, as far as you are concerned, you are only ever drawing to the back buffer and forget the front ever existed. If you want another surface to draw to, then you need to use an offscreen surface (pbuffer or FrameBuffer).
Therefore, to save what you have drawn this frame, right after you have finished with the draw routine, set the viewport dimensions to the texture width/height, bind the texture and call glCopyTexSubImage.

On some implemnetations, after swap buffers, the contents of the front buffer may be undefined - so this technique is flawed from the start.

When did I ever say I’m trying to draw to the front buffer? Like I’ve said a bunch of times now, I’m trying to copy the front buffer to the back buffer!

…and what do you think is in the front buffer? Usually (depending on your setup - perhaps you should post that) you have been drawing to the back buffer. GL then swaps this to the front buffer automatically. You are trying to fight the system by copying the front buffer to the back buffer.
If all you want is a copy of the current frame, then copy the back buffer to a texture - this is guaranteed to work.

What I think is in the front buffer is the last frame that was drawn.

I’m not “fighting the system” by copying the front buffer to the back buffer. After copying the front buffer to the back buffer I then draw the stuff that has changed, i.e. the dirty rectangles.

And I’ve already tried copying the back buffer to a texture before the glswap. Another guy suggested this only a few posts ago and I responded that it gives the same result: garbled image. And this wouldn’t do what I want anyway, as all that would be in the back buffer are the quads that have changed since the last frame.

I’m sure there are 1000’s of us who copy the back buffer to a texture on a regular basis - so this does work. In my render engine, as a matter of routine, if the GL client does not support FrameBufferObjects I fall back to glCopyTexSubImage2D. Therefore the garbage you are getting is a result of something else - not the copying of the back buffer to a texture.

Perhaps you can try giving a bit more detail to the process you are doing because I can’t see why you need to copy front to back buffer when the system has already copied back buffer to front for you. Can you explain what you mean by the dirty rectangles a bit more?

I’ve already explained it 4-5 times. I don’t know how else to explain it, but I’ll try again…

My program emulates a 80x50 console. That’s 4000 characters (80x50). In the case of OpenGL, 4000 quads that need to be drawn each frame, as I’m drawing each character as a separate quad. That takes a long time. What I want to do (and what I’ve been doing for 2 years with SDL) is only having to draw those of the 4000 quads that have changed since the last frame (I keep track of this in my code). If I only draw the quads that have changed to the back buffer, all the quads that haven’t changed won’t be drawn, as the back buffer has been cleared since the last frame. That’s why I first want to draw the contents of the screen (the last frame) to the back buffer and THEN draw those of the 4000 quads that have changed.

try giving your texture trash data before using or simply glTexImage2D(…,NULL);

I’m initializing the textures for saving the screen like this in the beginning of my program. Am I perhaps doing something wrong here?

   FOR I := 1 TO 5 DO BEGIN
      FOR J := 1 TO 4 DO BEGIN
         glGenTextures(1, @ScreenTextureID[I, J]);
         glBindTexture(GL_TEXTURE_2D, ScreenTextureID[I, J]);
         glTexImage2D(GL_TEXTURE_2D, 0, 4, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, NIL);

(That’s Pascal code, by the way.)

  1. create a texture (refered later to textureA) to hold the whole framebuffer, no need to fill it now, as said AK47.
  2. draw all quads normally
  3. glCopyTexSubImage the whole viewport to textureA
  4. swapbuffers
  5. – new frame:
  6. render large quad with textureA
  7. redraw the parts that have changed
  8. GOTO 2)

As others said, never mess with the frontbuffer, it does not even exists on composited desktops.

Now, at which step do you still have a problem ?