16

I want to copy texture1 to texture2. The most stupid way is copying tex1 data from GPU to CPU, and then copy CPU data to GPU. The stupid code is as below:

float *data = new float[width*height*4];
glBindTexture(GL_TEXTURE_2D, tex1);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, data);
glBindTexture(GL_TEXTURE_2D, tex2]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, data);

As I know, it must exist a method that supports data copying from GPU tex to GPU tex without CPU involved. I consider about using FBO that rendering a tex1 quad to tex2. But somehow I think it is still naive. So what is the most efficient way to implement this?

rtrobin
  • 270
  • 2
  • 11
  • 1
    glCopyTexImage2D copy from color buffer to texture. I think it should have a way that avoids rendering to color buffer or frame buffer. – rtrobin Apr 19 '13 at 08:26
  • Color buffer in FBO can be also a texture. – Michael IV Apr 19 '13 at 08:29
  • You mean using FBO renderingToTexture? I know this could work. I think there should be a better way. Like in CUDA, we could use cudamemcpy(..,cudaMemcpyDeviceToDevice) – rtrobin Apr 19 '13 at 08:35
  • Since CUDA ist NV specific and would basically do the same, I assume FBO usage is better were possible. – thewhiteambit Oct 19 '15 at 14:51
  • Possible duplicate of [In openGL. How do you copy part of a texture to another texture](http://stackoverflow.com/questions/17682799/in-opengl-how-do-you-copy-part-of-a-texture-to-another-texture) – thewhiteambit Oct 20 '15 at 11:59

1 Answers1

22

If you have support for OpenGL 4.3, there is the straight-forward glCopyImageSubData for exactly this purpose:

glCopyImageSubData(tex1, GL_TEXTURE_2D, 0, 0, 0, 0,
                   tex2, GL_TEXTURE_2D, 0, 0, 0, 0,
                   width, height, 1);

Of course this requires the destination texture to already be allocated with an image of appropriate size and format (using a simple glTexImage2D(..., nullptr), or maybe even better glTexStorage2D if you have GL 4 anyway).


If you don't have that, then rendering one texture into the other using an FBO might still be the best approach. In the end you don't even need to render the source texture. You can just attach both textures to an FBO and blit one color attachment over into the other using glBlitFramebuffer (core since OpenGL 3, or with GL_EXT_framebuffer_blit extension in 2.x, virtually anywhere where FBOs are in the first place):

glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 
                       GL_TEXTURE_2D, tex1, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, 
                       GL_TEXTURE_2D, tex2, 0);
glDrawBuffer(GL_COLOR_ATTACHMENT1);
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, 
                  GL_COLOR_BUFFER_BIT, GL_NEAREST);

Of course if you do that multiple times, it might be a good idea to keep this FBO alive. And likewise this also requires the destination texture image to have the appropriate size and format beforehand. Or you could also use Michael's suggestion of only attaching the source texture to the FBO and doing a good old glCopyTex(Sub)Image2D into the destination texture. Needs to be evaluated which performs better (if any).


And if you don't even have that one, then you could still use your approach of reading one texture and writing that data into the other. But instead of using the CPU memory as temporary buffer, use a pixel buffer object (PBO) (core since OpenGL 2.1). You will still have an additional copy, but at least that will (or is likely to be) a GPU-GPU copy.

Christian Rau
  • 45,360
  • 10
  • 108
  • 185
  • Are you sure this function isn't available also for 4.0 , 4.1 ,4.2 ? – Michael IV Apr 19 '13 at 09:05
  • 1
    @MichaelIV It is most probably available for any 4.x by a back-ported core extension (but it's only core in 4.3), but then again, chosing between a particular 4.x is usually just a driver question anyway. If you have 4.1, chances are you have 4.3 after istalling the most recent drivers (given that you're using hardware with reasonable drivers in the first place, like xXxxxx, and not something like XXX rubbish ;)). – Christian Rau Apr 19 '13 at 09:08
  • @Christian Rau. sooooo great! I think you explain all. Furthermore, I wanna ask: 1).Do I need to create GL4 or GL3 context to use first two methods? Or I could just simply use these functions in my existing code? 2).My graphic card supports OpenGL3.3. So is glCopyImageSubData not available for me in anyway? I found function [CopyImageSubDataNV](http://www.opengl.org/registry/specs/NV/copy_image.txt). Is this available for my card? – rtrobin Apr 19 '13 at 11:29
  • @rtrobin `glCopySubImageData` will most probably not be available. It is only core since GL 4.3 and I think even as extension it is only available on 4-class hardware. That aside generally if you create your GL context as usual, you get highest version supported by your graphics card **and** driver. You don't have to explicitly request a GL 3.3 context, that is only required if you want a core context (without deprecated back-ward functionality, but none of the above functions are deprecated anyway). So if you got the newest driver, you will at least have access to the second method. – Christian Rau Apr 19 '13 at 11:50
  • @rtrobin But you can also just look if your implementation supports the [GL_ARB_copy_image extension](http://www.opengl.org/registry/specs/ARB/copy_image.txt). Technically it requires only GL 1.1, but practically you will at least need CUDA-hardware, so it might even be supported on OpenGL 3 hardware. – Christian Rau Apr 19 '13 at 11:53
  • @rtrobin You can check for extensions by just querying `glGetString(GL_EXTENSIONS)` and looking for the extension name in it. But of course to actually use this extension you need some kind of extension loading library (if you don't want to do it yourself, which I wouldn't suggest), like *GLEW*. But since you need that anyway for any OpenGL functionality newer than 1.1 (at least in *"Winix"*, *Mac* seems to work different in this regard), this should already be present anyway. – Christian Rau Apr 19 '13 at 11:57
  • @ChristianRau: `glGetString(GL_EXTENSIONS)` was *removed* in 3.1. You should be [using `glGetStringi` to get each individual extension string](http://www.opengl.org/wiki/Get_Context_Info#Extension_list). – Nicol Bolas Apr 20 '13 at 11:05
  • @NicolBolas Oh right, I thought that change came only with 4+. – Christian Rau Apr 20 '13 at 11:47
  • @ChristianRau :Is it necessary to generate two frameBuffers for GL3 card? I tested that using 1 FBO, binding tex1 to readBuffer and tex2 to writeBuffer, then bliting. The result also seemed right. – rtrobin Apr 20 '13 at 16:19
  • @rtrobin Do you mean using the same FBO as source and destination in `glBlitFramebuffer` and selecting one color attachment as read buffer and another one as write buffer? Well, not sure if that works, but when you say it does, then it seems to. – Christian Rau Apr 20 '13 at 19:56
  • @ChristianRau Let me show you my code. `glBindFramebuffer(GL_FRAMEBUFFER,fb); GLenum buf[1]={GL_COLOR_ATTACHMENT1}; glDrawBuffers(1,buf); glFramebufferTexture2D(GL_READ_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,tex0,0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,tex1,0); glBlitFramebuffer(0,0,width,height,0,0,width,height,GL_COLOR_BUFFER_BIT,GL_NEAREST); glBindFramebuffer(GL_FRAMEBUFFER,0); ` – rtrobin Apr 21 '13 at 07:24
  • @rtrobin Yeah, that's what I thought from your comment. Nice to know that this works (I looked into the spec and it indeed seems to be absolutely no problem, in the end one could even copy from one region of the same buffer to another). Probably better than creating two FBOs. By the way, using `glDrawBuffer` (wthout the *"s"* at the end) would spare you the creation of the `buf` array and thus one line of code. – Christian Rau Apr 21 '13 at 09:51