murrdpirate's picture

Copy OpenGL texture to Bitmap in GPU memory using OpenCL

I need to share a texture between two OpenGL contexts. One OpenGL context is my own GameWindow, the other is a 3rd party API view that I host on a Windows form. I don't have access to its OpenGL context, so I don't think there is any way for me to share resources between this 3rd party API and my GameWindow. However, I can copy a texture from this API into main memory as a bitmap, using the following routine:

// render the left STK view
            // copy the render from the back buffer into textureLstk
            GL.BindTexture(TextureTarget.Texture2D, textureLstk);
            GL.CopyTexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgb, 0, 0, 640, 800, 0);
            // copy textureLstk into a bitmap in main memory
            dataL = bmL.LockBits(new Rectangle(0, 0, bmL.Width, bmL.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            GL.GetTexImage(TextureTarget.Texture2D, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgr, PixelType.UnsignedByte, dataL.Scan0);

Then I can load this Bitmap into a texture in my GameWindow context. Of course, working with main memory is very slow, so I was wondering if it would be possible to write the Bitmap to an address in GPU memory using OpenCL. Any ideas?


Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

You can use CL_KHR_GL_sharing extension to share resources between OpenGL and OpenCL.

However, before you go down the road of OpenCL, are you certain that the copy of the texture to main memory is a bottleneck? A modern system can transfer between 1 to 6 GB/s of data between system and video memory. For comparison, a 640x800 32bpp image @60fps requires just 0.11 GB/s.

Note that there is no reason to copy the image into a System.Drawing.Bitmap if you just wish to re-upload it to a different context. A simple byte array will be more efficient: byte[width * height * 4].

That said, there are other ways to avoid the download to system memory:

  1. You can share resources between the two graphics contexts if you create the GameWindow *after* the 3rd party view, with a compatible GraphicsMode. OpenTK shares resources by default - you can control this via the GraphicsContext.ShareContexts property. The trick here is that you need to create the GameWindow after the 3rd party view is created, before the 3rd party view creates any OpenGL resources.
  2. Depending on the way the 3rd party view works, you could even try substituting its context with your own GraphicsContext. This is easier than it looks:

    GraphicsContext.ShareContexts = true;
    var form = new FormWithThirdPartyView();
    var window_info = OpenTK.Utilities.CreateWindowsWindowInfo(form.ThirdPartyControl.Handle);
    var my_own_context = new GraphicsContext(GraphicsMode.Default, window_info); 
    var gw = new GameWindow();
    // my_own_context and gw.Context are now shared
  3. Alternatively, you could use a single context to render both the 3rd party view and your GameWindow. The same code as above, but instead of constructing a new GraphicsContext, you would call gw.Context.MakeCurrent(window_info) using the IWindowInfo for the 3rd party control. This avoids the texture copy in the first place - you can just access it directly! The downside is you must take care to avoid trampling the rendering state of the 3rd party control.
murrdpirate's picture

Hmm, I guess my system is slower than I realized. The call to GL.GetTexImage for the 640x800 24bpp image takes at least 3-4 ms, or about 0.5 GB/s. I actually have two instances of the 3rd party view that I need to copy over (left and right views for Oculus Rift), and the images need to be even bigger to account for the distortion, and then doubled for when the Rift DK2 comes out. So it looks like I'd get about 30 fps at best.

In the first approach, you mentioned that I need to create my GameWindow after the 3rd party view is created, but before any OpenGL resources are created. I assume that's true for the other two methods also, is that right? My concern is that I don't think I can access such a state. In order to start seeing some graphics, I need to call either NewScenario() or LoadScenario() from the 3rd party control. I suspect that these calls create the OpenGL context and also load resources. If that's the case, would that mean it's impossible to share resources with my GameWindow?

Fiddler, I really appreciate the help you've given me on both this and the Oculus Rift. I don't yet have the skills to contribute code to OpenTK, but I could make a small financial donation if there is a means of doing so.