pbuffer vs fbo in egl offscreen rendering


https://stackoverflow.com/questions/28817777/pbuffer-vs-fbo-in-egl-offscreen-rendering


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();
}
share improve this question
 
 
Why aren't you checking the eglMakeCurrent() return value? –  genpfault  Mar 2 '15 at 19:48
 
I checked, and also checked eglGetError(), everything looks fine. I didn't paste them here for being concise. –  Bill Yan  Mar 2 '15 at 21:01

2 Answers

up vote 2 down vote accepted

There are a couple of problems in this code:

  1. The EGL_SURFACE_TYPE specified in the config attributes is wrong:

    pi32ConfigAttribs[0] = EGL_SURFACE_TYPE;
    pi32ConfigAttribs[1] = EGL_WINDOW_BIT;
    

    To render to a PBuffer, this needs to use the matching value:

    pi32ConfigAttribs[0] = EGL_SURFACE_TYPE;
    pi32ConfigAttribs[1] = EGL_PBUFFER_BIT;
    
  2. No size is specified for the PBuffer. While the man page suggests that it's legal to create a PBuffer without specifying a size, the defaults for width and height are 0. I can't imagine that anything good will happen when trying to render to a surface of size 0 times 0. To specify a size:

    EGLint pbufferAttribs[5];
    pbufferAttribs[0] = EGL_WIDTH;
    pbufferAttribs[1] = DesiredWidthOfPBuffer;
    pbufferAttribs[2] = EGL_HEIGHT;
    pbufferAttribs[3] = DesiredHeightOfPBuffer;
    pbufferAttribs[4] = EGL_NONE;
    eglSurface = eglCreatePbufferSurface(eglDisplay, eglConfig, pbufferAttribs);
    

My answer to this question has complete code to create a context and PBuffer surface for two different versions of EGL: GLES10.glGetIntegerv returns 0 in Lollipop only. The code uses the Java bindings, but it should be easy to adapt.

share improve this answer
 
 
Thank you so much, after editing my code based on your suggestion, I managed to make it work with opengl es 2 context. the image I read back is indeed in red. However, once I change opengl es to desktop opengl, I still have issues. the image I read back is now completely transparent. I tried to set buffer size, depth size and everything I think necessary, but I still can't fix the issue. :( my code is updated in the question. –  Bill Yan  Mar 3 '15 at 5:59

Why you are doing eglSwapBuffers before glReadPixels? Presumably eglSwapBuffers has no effect on PBufferSurface (since it is not double-buffer surface) but if it is you would try to read pixels from undefined buffer, with undefined result..


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值