title: pbuffer vs fbo in egl offscreen rendering
body: <p>I'm very confused at egl pbuffer surface. In my opinion, a pbuffer surface is a platform independent surface, just like the windows surface or the pixmap surface. Things drawn to that surface, although not visible, should be able to be read back.</p>
  
  <p>the answer to this question seems to confirm my understanding:</p>
  
  <p><a href="https://stackoverflow.com/questions/12906971/difference-from-eglcreatepbuffersurface-and-eglcreatepixmapsurface-with-opengl-e">Difference from eglCreatePbufferSurface and eglCreatePixmapSurface with OpenGL ES(EGL)</a></p>
  
  <p>however, my experiment shows that I need to create a fbo buffer in addition to using pbuffer surface.</p>
  
  <p>this code seems to work for me, it creates a pbuffer surface and then a fbo.</p>
  
  <pre><code>#include &lt;GLES2/gl2.h&gt;
  #include &lt;EGL/egl.h&gt;
  
  int main(int argc, char *argv[])
  {
      EGLint ai32ContextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2,
                                                       EGL_NONE };
      // Step 1 - Get the default display.
      EGLDisplay eglDisplay = eglGetDisplay((EGLNativeDisplayType)0);
      // Step 2 - Initialize EGL.
      eglInitialize(eglDisplay, 0, 0);
      // Step 3 - Make OpenGL ES the current API.
      eglBindAPI(EGL_OPENGL_ES_API);
      // Step 4 - Specify the required configuration attributes.
      EGLint pi32ConfigAttribs[5];
      pi32ConfigAttribs[0] = EGL_SURFACE_TYPE;
      pi32ConfigAttribs[1] = EGL_WINDOW_BIT;
      pi32ConfigAttribs[2] = EGL_RENDERABLE_TYPE;
      pi32ConfigAttribs[3] = EGL_OPENGL_ES2_BIT;
      pi32ConfigAttribs[4] = EGL_NONE;
      // Step 5 - Find a config that matches all requirements.
      int iConfigs;
      EGLConfig eglConfig;
      eglChooseConfig(eglDisplay, pi32ConfigAttribs, &amp;eglConfig, 1, &amp;iConfigs);
  
      if (iConfigs != 1) 
      {
          printf("Error: eglChooseConfig(): config not found.\n");
          exit(-1);
      }
  
      // Step 6 - Create a surface to draw to.
      EGLSurface eglSurface;
      eglSurface = eglCreatePbufferSurface(eglDisplay, eglConfig, NULL);
  
      // Step 7 - Create a context.
      EGLContext eglContext;
      eglContext = eglCreateContext(eglDisplay, eglConfig, NULL, ai32ContextAttribs);
  
      // Step 8 - Bind the context to the current thread
      eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
  
      GLuint fboId = 0;
      GLuint renderBufferWidth = 1920;
      GLuint renderBufferHeight = 1080;
  
      // Step 9 - create a framebuffer object
      glGenFramebuffers(1, &amp;fboId);
      glBindFramebuffer(GL_FRAMEBUFFER, fboId);
  
      GLuint renderBuffer;
      glGenRenderbuffers(1, &amp;renderBuffer);
      glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);
  
      glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB565, renderBufferWidth, renderBufferHeight);
      glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderBuffer);
  
      GLuint depthRenderbuffer;
      glGenRenderbuffers(1, &amp;depthRenderbuffer);
      glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
      glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, renderBufferWidth, renderBufferHeight);
      glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
  
      // Step 10 - check FBO status
      GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
      if(status != GL_FRAMEBUFFER_COMPLETE) 
      {
          printf("Problem with OpenGL framebuffer after specifying color render buffer: \n%x\n", status);
      } 
      else 
      {
          printf("FBO creation succedded\n");
      }
  
      int size = 4 * renderBufferHeight * renderBufferWidth;
      unsigned char *data2 = new unsigned char[size];
      // Step 11 - clear the screen in Red and read it back
      glClearColor(1.0,0.0,0.0,1.0);
      glClear(GL_COLOR_BUFFER_BIT);
      eglSwapBuffers( eglDisplay, eglSurface);
      glReadPixels(0,0,renderBufferWidth,renderBufferHeight,GL_RGBA, GL_UNSIGNED_BYTE, data2);
  
     ... save data2 to image ...
  }
  </code></pre>
  
  <p>However if I remove the fbo, and try to draw to the pbuffer directly, I will see a segmentation fault immediately after calling glClear() function:</p>
  
  <pre><code>#include &lt;GLES2/gl2.h&gt;
  #include &lt;EGL/egl.h&gt;
  
  int main(int argc, char *argv[])
  {
      EGLint ai32ContextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2,
                                                       EGL_NONE };
      // Step 1 - Get the default display.
      EGLDisplay eglDisplay = eglGetDisplay((EGLNativeDisplayType)0);
      // Step 2 - Initialize EGL.
      eglInitialize(eglDisplay, 0, 0);
      // Step 3 - Make OpenGL ES the current API.
      eglBindAPI(EGL_OPENGL_ES_API);
      // Step 4 - Specify the required configuration attributes.
      EGLint pi32ConfigAttribs[5];
      pi32ConfigAttribs[0] = EGL_SURFACE_TYPE;
      pi32ConfigAttribs[1] = EGL_WINDOW_BIT;
      pi32ConfigAttribs[2] = EGL_RENDERABLE_TYPE;
      pi32ConfigAttribs[3] = EGL_OPENGL_ES2_BIT;
      pi32ConfigAttribs[4] = EGL_NONE;
      // Step 5 - Find a config that matches all requirements.
      int iConfigs;
      EGLConfig eglConfig;
      eglChooseConfig(eglDisplay, pi32ConfigAttribs, &amp;eglConfig, 1, &amp;iConfigs);
  
      if (iConfigs != 1) 
      {
          printf("Error: eglChooseConfig(): config not found.\n");
          exit(-1);
      }
  
       // Step 6 - Create a surface to draw to.
       EGLSurface eglSurface;
       eglSurface = eglCreatePbufferSurface(eglDisplay, eglConfig, NULL);
  
       // Step 7 - Create a context.
       EGLContext eglContext;
       eglContext = eglCreateContext(eglDisplay, eglConfig, NULL, ai32ContextAttribs);
  
       // Step 8 - Bind the context to the current thread
       eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
  
       int size = 4 * renderBufferHeight * renderBufferWidth;
       unsigned char *data2 = new unsigned char[size];
       // Step 11 - clear the screen in Red and read it back
       glClearColor(1.0,0.0,0.0,1.0);
       glClear(GL_COLOR_BUFFER_BIT);
       eglSwapBuffers(    eglDisplay, eglSurface);
       glReadPixels(0,0,renderBufferWidth,renderBufferHeight,GL_RGBA, GL_UNSIGNED_BYTE, data2);
  
       ... save data2 to image ...
  }
  </code></pre>
  
  <p>My environment is ubuntu 14 with intel graphics/mesa.</p>
  
  <p>Do you know why I saw the segmentation fault? (I checked the eglcontext, which seems to be created successfully.) Can you confirm that fbo is needed with pbuffer surface?</p>
  
  <p><strong>EDIT: as pointed out by Reto, my problem is because of missing attributes.</strong></p>
  
  <p>after setting those attributes, I was able to make things work with opengl es 2 context. However, I still have issues with the desktop opengl context.</p>
  
  <p>Instead of reading back a red image, with the desktop opengl context, I could only get a transparent image. here is my current code:</p>
  
  <pre><code>#include &lt;QCoreApplication&gt;
  #include &lt;QDebug&gt;
  #include &lt;QImage&gt;
  #include &lt;GL/gl.h&gt;
  #include &lt;EGL/egl.h&gt;
  #include &lt;QElapsedTimer&gt;
  
  int main(int argc, char *argv[])
  {
      // Step 1 - Get the default display.
      EGLDisplay eglDisplay = eglGetDisplay((EGLNativeDisplayType)0);
  
      // Step 2 - Initialize EGL.
      eglInitialize(eglDisplay, 0, 0);
  
      // Step 3 - Make OpenGL ES the current API.
      eglBindAPI(EGL_OPENGL_API);
  
      // Step 4 - Specify the required configuration attributes.
      EGLint pi32ConfigAttribs[5];
      pi32ConfigAttribs[0] = EGL_SURFACE_TYPE;
      pi32ConfigAttribs[1] = EGL_PBUFFER_BIT;
      pi32ConfigAttribs[2] = EGL_RENDERABLE_TYPE;
      pi32ConfigAttribs[3] = EGL_OPENGL_BIT;
      pi32ConfigAttribs[4] = EGL_CONFORMANT;
      pi32ConfigAttribs[5] = EGL_OPENGL_BIT;
      pi32ConfigAttribs[6] = EGL_COLOR_BUFFER_TYPE;
      pi32ConfigAttribs[7] = EGL_RGB_BUFFER;
      pi32ConfigAttribs[8] = EGL_LUMINANCE_SIZE;
      pi32ConfigAttribs[9] = 0;
      pi32ConfigAttribs[10] = EGL_RED_SIZE;
      pi32ConfigAttribs[11] = 8;
      pi32ConfigAttribs[12] = EGL_GREEN_SIZE;
      pi32ConfigAttribs[13] = 8;
      pi32ConfigAttribs[14] = EGL_BLUE_SIZE;
      pi32ConfigAttribs[15] = 8;
      pi32ConfigAttribs[16] = EGL_ALPHA_SIZE;
      pi32ConfigAttribs[17] = 8;
      pi32ConfigAttribs[18] = EGL_DEPTH_SIZE;
      pi32ConfigAttribs[19] = 8;
      pi32ConfigAttribs[20] = EGL_LEVEL;
      pi32ConfigAttribs[21] = 0;
      pi32ConfigAttribs[22] = EGL_BUFFER_SIZE;
      pi32ConfigAttribs[23] = 24;
      pi32ConfigAttribs[24] = EGL_NONE;
      // Step 5 - Find a config that matches all requirements.
      int iConfigs;
      EGLConfig eglConfig;
      eglChooseConfig(eglDisplay, pi32ConfigAttribs, &amp;eglConfig, 1, &amp;iConfigs);
      qDebug() &lt;&lt; "egl error" &lt;&lt; eglGetError();
  
      if (iConfigs != 1)
      {
          printf("Error: eglChooseConfig(): config not found.\n");
          exit(-1);
      }
  
      EGLint pbufferAttribs[5];
      pbufferAttribs[0] = EGL_WIDTH;
      pbufferAttribs[1] = 1920;
      pbufferAttribs[2] = EGL_HEIGHT;
      pbufferAttribs[3] = 1080;
      pbufferAttribs[4] = EGL_NONE;
  
      // Step 6 - Create a surface to draw to.
      EGLSurface eglSurface;
      eglSurface = eglCreatePbufferSurface(eglDisplay, eglConfig, pbufferAttribs);
      qDebug() &lt;&lt; "egl error" &lt;&lt; eglGetError();
  
      if (eglSurface == EGL_NO_SURFACE)
      {
          qDebug() &lt;&lt; "surface issue";
      }
  
      // Step 7 - Create a context.
      EGLContext eglContext;
      eglContext = eglCreateContext(eglDisplay, eglConfig, NULL, NULL);
      qDebug() &lt;&lt; "egl error" &lt;&lt; eglGetError();
  
      if (eglContext == EGL_NO_CONTEXT)
      {
           qDebug() &lt;&lt; "context issue";
      }
  
      // Step 8 - Bind the context to the current thread
      bool result = eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
  
      if (!result)
      {
          qDebug() &lt;&lt; "make current error" &lt;&lt; eglGetError();
      }
      qDebug() &lt;&lt; "egl error" &lt;&lt; eglGetError();
  
      GLuint renderBufferWidth = 1920;
      GLuint renderBufferHeight = 1080;
  
      QElapsedTimer benchmarkTimer;
      int size = 4 * renderBufferHeight * renderBufferWidth;
      unsigned char *data2 = new unsigned char[size];
      int i = 0;
      benchmarkTimer.start();
      while(i&lt;1000)
      {
          glClearColor(1.0,0.0,0.0,1.0);
          glClear(GL_COLOR_BUFFER_BIT);
          eglSwapBuffers( eglDisplay, eglSurface);
          glReadPixels(0,0,renderBufferWidth,renderBufferHeight,GL_RGBA, GL_UNSIGNED_BYTE, data2);
          ++i;
      }
      qDebug() &lt;&lt; "fps" &lt;&lt; 1000.0*1000.0/benchmarkTimer.elapsed();
  
      QImage saveImage(data2, renderBufferWidth, renderBufferHeight, QImage::Format_RGBA8888_Premultiplied);
      saveImage.save("haha.png");
      QCoreApplication a(argc, argv);
      qDebug() &lt;&lt; "done";
      return a.exec();
  }
  </code></pre>