We recently encountered the need to copy OpenGL textures. Do not ask, there are situations, in which copying a texture is the best solution.

My first attempt at this was to use pixel buffer objects, that is, create a sufficient large PBO, copy the texture to it using glGetTexImage, bind a new texture and copy it there using glTexImage2D. This was horribly slow, around 22ms for a 1920x1080 texture on a GeForce 88oo Ultra. I still wonder why.

Next try was to use framebuffer objects, which can be done pretty straight forward:

 1 /* Assume "fbo" is a name of a FBO created using glGenFramebuffersEXT(1, &fbo),
 2  * and width/height are the dimensions of the texture, respectively.
 3  * "tex_src" is the name of the source texture, and
 4  * "tex_dst" is the name of the destination texture, which should have been
 5  * already created */ 
 6 
 7 /// bind the FBO
 8 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
 9 /// attach the source texture to the fbo
10 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
11                         GL_TEXTURE_2D, tex_src, 0);
12 /// bind the destination texture
13 glBindTexture(GL_TEXTURE_2D, tex_dst);
14 /// copy from framebuffer (here, the FBO!) to the bound texture
15 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width, height);
16 /// unbind the FBO
17 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

This works very well and very fast, less than 0.1 ms. However, you need to create a new FBO for every different texture dimensions.

Knowing this, I tried the same approach to copy a texture into a pixel buffer object, which also shows to be quite faster this way:

 1 /// Again, bind fbo and attach source texture
 2 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
 3 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
 4         GL_TEXTURE_2D, tex_src, 0);
 5 
 6 /// read pixels into pbo from framebuffer
 7 /// note you obviously need to specify a format and type
 8 glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
 9 glReadPixels(0,0,width,height,format,type,0);
10 
11 /// unbind everything as we are done
12 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
13 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

There's of course no guarantee that these approaches are the fastest for every hardware and set-up. If you have something working faster for you, it would be nice to hear of that!