1

When I call glReadPixels on another thread, it doesn't return me any data. I read somewhere suggesting that I need to create a new context in the calling thread and copy the memory over. How exactly do I do this?

This is the glReadPixels code I use:

pixels = new BYTE[ 3 * width * height];
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels);
image = FreeImage_ConvertFromRawBits(pixels, width, height, 3 * width, 24, 0xFF0000, 0x00FF00, 0x0000FF, false);
FreeImage_Save(FIF_PNG, image, pngpath.c_str() , 0);

Alternatively, I read from this thread they suggest to use another piece of code (see the end) but I dont understand what are origX, origY, srcOrigX, srcOrigY?

huy
  • 4,782
  • 6
  • 36
  • 42

3 Answers3

4

You can create shared contexts, and this will work as you intended. See wglShareLists (the name is chosen badly, it shares more than just lists). Or, use WGL_ARB_create_context, which directly supports sharing contexts too (you have tagged the question "windows", but similar functionality exists for non-WGL too).

However, it is much, much easier to use a pixel buffer object instead, that will have the same net effect as multithreading (the transfer will run asynchronously without blocking the render thread), and it is many times less complex.

user771921
  • 141
  • 4
3

You have different options.

You call ReadPixel pipelined with the rendering thread. In this case the returned data shall be stored on a buffer that can be enqueued to a thread that is dedcated for saving pictures. This can be done easily with a buffer queue, a mutex and a semaphore: rendering thread get data using ReadPixel, lock the mutex, enqueue (system memory) pixel buffer, unlock the mutex, increase the semaphore; the worker thread (locked on the semaphore) will be signaled by the rendering thread, lock the mutex, dequeue the pixel buffer, unlock the mutex and save the image.

Otherwise, you can copy the current framebuffer on a texture or a pixel buffer object. In this case you must have two different threads, having an OpenGL context current (via MakeCurrent) each, sharing their object space with each other (as suggested by user771921). When the first rendering thread calls ReadPixels (or CopyPixels), notifies the second thread about the operation (using a semaphore for example); the second rendering thread will map the pixel buffer object (or get the texture data). This method has the advantage to allow the driver to pipeline the first thread read operation, but it actually doubles the memory copy operations by introducing an additional support buffer. Moreover the ReadPixel operation is flushed when the second thread maps the buffer, which is executed (most probably) just after the second thread is signaled.

I would suggest the first option, since it is the much cleaner and simple. The second one is overcomplicated, and I doubt you can get advantages from using it: the image saving operation is a lot slower than ReadPixel.

Even if the ReadPixel is not pipelined, does your FPS really slow down? Don't optimize before you can profile.

The example you have linked uses GDI functions, which are not OpenGL related. I think the code would causea repaint form event and then capture the window client area contents. It seems to much slower compared with ReadPixel, even if I haven't actually executed any profiling on this issue.

Luca
  • 11,646
  • 11
  • 70
  • 125
1

Well, using opengl in a multi threaded program is a bad idea - specially if you use opengl functions in a thread that has no context created.

Apart from that, there is nothing wrong in your code example.

BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • Thanks. How would you suggest me go about doing this? – huy Sep 09 '11 at 13:46
  • @huy The simplest would be to read pixels from the thread in which you created the opengl context. The best would be not to have more then 1 thread in the program that uses opengl library. – BЈовић Sep 09 '11 at 13:55
  • 1
    @VJo: There's nothing wrong with multithreading a program that uses OpenGL. One should just keep all OpenGL operations to a single thread. – datenwolf Sep 09 '11 at 14:44
  • @datenwolf This is in the question : *glReadPixels on another thread, it doesn't return me any data*, which means that he created a opengl context in one thread and tried to read it in another. – BЈовић Sep 09 '11 at 14:55
  • @VJo: You wrote "The best would be not to have more then 1 thread in the program that uses opengl" which someone (I) may understand as "don't to multithreading, if using OpenGL". – datenwolf Sep 09 '11 at 15:15
  • @datenwolf Better interpretation is : "try to avoid using multithreading if using opengl". – BЈовић Sep 09 '11 at 15:19
  • @VJo: You can multithread as much as you like, as long as you keep an OpenGL context to only one thread at a time. So saying "avoid multithreading" just doesn't get the point. – datenwolf Sep 09 '11 at 15:36
  • 2
    @datewolf No. Actually there can be one current OpenGL context per thread, not per process (from MSDN: "A process can have multiple rendering contexts by means of multithreading. A thread must set a current rendering context before calling any OpenGL functions. Otherwise, all OpenGL calls are ignored."). – Luca Sep 10 '11 at 17:39