2

I need to take sceenshots at every frame and I need very high performance (I'm using freeGlut). What I figured out is that it can be done like this inside glutIdleFunc(thisCallbackFunction)

GLubyte *data = (GLubyte *)malloc(3 * m_screenWidth * m_screenHeight);
glReadPixels(0, 0, m_screenWidth, m_screenHeight, GL_RGB, GL_UNSIGNED_BYTE, data);
// and I can access pixel values like this: data[3*(x*512 + y) + color] or whatever

It does work indeed but I have a huge issue with it, it's really slow. When my window is 512x512 it runs no faster than 90 frames per second when only cube is being rendered, without these two lines it runs at 6500 FPS! If we compare it to irrlicht graphics engine, there I can do this

// irrlicht code
video::IImage *screenShot = driver->createScreenShot();
const uint8_t *data = (uint8_t*)screenShot->lock();
// I can access pixel values from data in a similar manner here

and 512x512 window runs at 400 FPS even with a huge mesh (Quake 3 Map) loaded! Take into account that I'm using openGL as driver inside irrlicht. To my inexperienced eye it seems like glReadPixels is copying every pixel data from one place to another while (uint8_t*)screenShot->lock() is just copying a pointer to already existent array. Can I do something similar to latter using freeGlut? I expect it to be faster than irrlicht.

Note that irrlicht uses openGL too (well it offers directX and other options as well but in the example I gave above I used openGL and by the way it was the fastest compared to other options)

Alex Azazel
  • 332
  • 5
  • 18
  • 1
    Look up Pixel Buffer Objects which let you do it asynchronously. – user253751 Oct 02 '16 at 21:43
  • 2
    If you put that code in the idle function, it might be called more than once per frame. Also, you're leaking memory if you never free the data you allocate. – Reto Koradi Oct 03 '16 at 01:10
  • @RetoKoradi well, no I don't literally execute these lines every time `idle` is called. `data` is allocate only once in the constructor and I use it over and over. Now I'm looking into PBO.... – Alex Azazel Oct 03 '16 at 15:24

1 Answers1

3

OpenGL methods are used to manage the rendering pipeline. In its nature, while the graphics card is showing image to the viewer, computations of the next frame are being done. When you call glReadPixels; graphics card wait for the current frame to be done, reads the pixels and then starts computing the next frame. Therefore pipeline becomes stalled and becomes sequential.

If you can hold two buffers and tell to the graphics card to read data into these buffers interchanging each frame; then you can read-back from your buffer 1-frame late but without stalling the pipeline. This is called double buffering. You can also do triple buffering with 2 frame late read-back and so on.

There is a relatively old web page describing the phenomenon and implementation here: http://www.songho.ca/opengl/gl_pbo.html

Also there are a lot of tutorials about framebuffers and rendering into a texture on the web. One of them is here: http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-14-render-to-texture/

zahir
  • 1,298
  • 2
  • 15
  • 35
  • So, I figured out how to render to a texture but I don't know how to move this texture to a main buffer so that it gets displayed on the screen and most importantly how to access pixel values from this texture using indices (as I showed in the question). What should I do? – Alex Azazel Oct 10 '16 at 20:08
  • You can use `glBlitFramebuffer` by binding the back buffer as `DRAW_BUFFER` and binding your texture framebuffer as `READ_BUFFER` apriori. This is for putting the image onto screen. To read pixel values you shoud use a pixel buffer object (a.k.a. PBO). – zahir Oct 10 '16 at 21:04
  • I did manage to move the image from frame buffer object (where I rendered my scene) to main buffer so that it gets displayed on the screen but I still can't access pixel values. I don't quite understand where PBO should come in. This tutorial (http://www.songho.ca/opengl/gl_pbo.html) doesn't seem so easy for me to follow, the source code is 600 lines long and looks really convoluted. What should I do? Ask another question about that specifically? I don't even know what to ask exactly. Any advice would be appreciated. – Alex Azazel Oct 12 '16 at 17:11
  • 1
    Sadly I don't have a working sample code. The basic idea behind http://www.songho.ca/opengl/gl_pbo.html is you bind one of 2 PBOs for `GL_PIXEL_PACK_BUFFER` and your `glReadPixels` become instant on the client side (your code), but you can only read from buffer if previous operations on the buffer is done on the server side (graphics card). Therefore you can read previous frame almost instantly. – zahir Oct 13 '16 at 16:18
  • by the way, I'm using integrated GPU (Intel HD graphics 4000), as far as I know CPU and GPU share the same memory so why is it that I need to download? Why is it impossible to get a pointer? – Alex Azazel Oct 14 '16 at 13:11
  • You can simulate this yourself. Just allocate a buffer of data, generate two threads where one is doing some expensive computation and writing to the buffer one by one and the other displays whole buffer to the console. Even that requires synchronization, and if you use two buffers things get faster. Now make it two processes where one of the process runs in kernel space and memory is managed by hardware. – zahir Oct 14 '16 at 15:27
  • I found some code http://lektiondestages.blogspot.com/2013/01/reading-opengl-backbuffer-to-system.html look at Method #3 is that what you were talking about? If yes then it's not any faster, I implemented it. – Alex Azazel Oct 14 '16 at 17:23
  • I just corrected the format and it's just as fast without asynchronous readback http://stackoverflow.com/questions/11409693/asynchronous-glreadpixels-with-pbo – Alex Azazel Oct 15 '16 at 16:22