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.
the answer to this question seems to confirm my understanding:
Difference from eglCreatePbufferSurface and eglCreatePixmapSurface with OpenGL ES(EGL)
however, my experiment shows that I need to create a fbo buffer in addition to using pbuffer surface.
this code seems to work for me, it creates a pbuffer surface and then a fbo.
#include <GLES2/gl2.h>
#include <EGL/egl.h>
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, &eglConfig, 1, &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, &fboId);
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
GLuint renderBuffer;
glGenRenderbuffers(1, &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, &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 ...
}
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:
#include <GLES2/gl2.h>
#include <EGL/egl.h>
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, &eglConfig, 1, &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 ...
}
My environment is ubuntu 14 with intel graphics/mesa.
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?
EDIT: as pointed out by Reto, my problem is because of missing attributes.
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.
Instead of reading back a red image, with the desktop opengl context, I could only get a transparent image. here is my current code:
#include <QCoreApplication>
#include <QDebug>
#include <QImage>
#include <GL/gl.h>
#include <EGL/egl.h>
#include <QElapsedTimer>
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, &eglConfig, 1, &iConfigs);
qDebug() << "egl error" << 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() << "egl error" << eglGetError();
if (eglSurface == EGL_NO_SURFACE)
{
qDebug() << "surface issue";
}
// Step 7 - Create a context.
EGLContext eglContext;
eglContext = eglCreateContext(eglDisplay, eglConfig, NULL, NULL);
qDebug() << "egl error" << eglGetError();
if (eglContext == EGL_NO_CONTEXT)
{
qDebug() << "context issue";
}
// Step 8 - Bind the context to the current thread
bool result = eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
if (!result)
{
qDebug() << "make current error" << eglGetError();
}
qDebug() << "egl error" << 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<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() << "fps" << 1000.0*1000.0/benchmarkTimer.elapsed();
QImage saveImage(data2, renderBufferWidth, renderBufferHeight, QImage::Format_RGBA8888_Premultiplied);
saveImage.save("haha.png");
QCoreApplication a(argc, argv);
qDebug() << "done";
return a.exec();
}
eglMakeCurrent()
return value? – genpfault Mar 2 '15 at 19:48