1

My C++ code was designed for iOS and now I ported it to NDK with minimal modifications.

I bind frame buffer and call

glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

then I bind main frame buffer like this

glBindFramebuffer(GL_FRAMEBUFFER, 0);

glGetError() returns GL_INVALID_FRAMEBUFFER_OPERATION

I can draw in my framebuffer and use its texture to draw it in main framebuffer. But I when I call glReadPixels then I get zeros

That code worked in iOS and most works in Android except glReadPixels

glCheckFramebufferStatus(GL_FRAMEBUFFER) returns GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7

--

I will consider sample code that will give me pixels data from framebuffer or texture that I can save to file as an answer

Right now I can draw to buffer with attached texture and I can use that texture to draw on main buffer. But I can't get pixels from framebuffer/texture to save to file or post to facebook.

Max
  • 6,286
  • 5
  • 44
  • 86
  • 1
    What's the result of glGetError() right after binding the framebuffer and after glReadPixels respectively? – Rick77 Mar 23 '13 at 21:47
  • 0 and 1268 (0x506) GL_INVALID_FRAMEBUFFER_OPERATION I am going to query `glCheckFramebufferStatus` now – Max Mar 23 '13 at 22:23
  • 1
    Are you sure that you are not inadvertently leveraging iOs extensions not present on Android (e.g. non power of two textures or some unsupported format for your color/depth buffers)? Does glGetError returns anything special after you create each texture? I'm not much of an OpenGL Expert, but spraying my code with assert(glGetError() == 0) in the most unexpected places always yielded interesting results for me... :) – Rick77 Mar 23 '13 at 22:52
  • I was using NPOT texture and it worked. I was able to draw framebuffers NPOT texture on screen. But now I have 512x512 teture and it does not help to get `glReadPixels` working. Right now I am adding `glGetError()` here and there – Max Mar 23 '13 at 23:01
  • when I create that buffer I get GL_FRAMEBUFFER_COMPLETE – Max Mar 23 '13 at 23:04
  • mind that I'm really making wild guesses, but some suggest that Android only supports RGBA888 whith glReadPixels (http://stackoverflow.com/questions/6802266/how-to-use-glreadpixels-with-android-usually-get-zeros?rq=1). Also try using GL_DEPTH_COMPONENT16 and GL_STENCIL_INDEX8 for the renderbuffers and see if something changes... Good luck! – Rick77 Mar 24 '13 at 00:40
  • `GLES 2.0` does not define such value. So I can't pass it to `glReadPixels` – Max Mar 26 '13 at 16:31
  • sorry for the mistake, I should have checked the source more carefully. I hope to fare better with my answer. – Rick77 Mar 29 '13 at 23:03
  • What does your EGLConfig look like? – fadden Mar 29 '13 at 23:31

1 Answers1

2

I have been able to do a glReadPixels from a framebuffer modifying the NDK example "GL2JNIActivity".

I just changed gl_code.cpp, where I have added an initialization function:

char *pixel = NULL;
GLuint fbuffer, rbuffers[2];
int width, height;
bool setupMyStuff(int w, int h) {
     // Allocate the memory
     if(pixel != NULL) free(pixel);
     pixel = (char *) calloc(w * h * 4, sizeof(char)); //4 components
     if(pixel == NULL)  {
                  LOGE("memory not allocated!");
                       return false;
                       }

                       // Create and bind the framebuffer
    glGenFramebuffers(1, &fbuffer);
    checkGlError("glGenFramebuffer");

    glBindFramebuffer(GL_FRAMEBUFFER, fbuffer);
    checkGlError("glBindFramebuffer");

    glGenRenderbuffers(2, rbuffers);
    checkGlError("glGenRenderbuffers");

    glBindRenderbuffer(GL_RENDERBUFFER, rbuffers[0]);
    checkGlError("glGenFramebuffer[color]");

    glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB565, w, h);
    checkGlError("glGenFramebuffer[color]");

    glBindRenderbuffer(GL_RENDERBUFFER, rbuffers[1]);
    checkGlError("glGenFramebuffer[depth]");

    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, w, h);
    checkGlError("glGenFramebuffer[depth]");

    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbuffers[0]);
    checkGlError("glGenFramebuffer[color]");
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,  GL_RENDERBUFFER, rbuffers[1]);
    checkGlError("glGenFramebuffer[depth]");

    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
                                                    LOGE("Framebuffer not complete");
                                                    return false;
    }

    // Turn this on to confront the results in pixels when the fb is active with the ones obtained from the screen
    if(false) {
                  glBindFramebuffer(GL_FRAMEBUFFER, 0);
        checkGlError("glBindFramebuffer");
    }

    width = w;
    height = h;

    return true;
}

which does nothing more than initializing the framebuffer and allocating the space for the output, and which I call at the very end of

bool setupGraphics(int w, int h);

and a block to save the output in my pixels array (which I obviously execute after the

    checkGlError("glDrawArrays");

statement in renderFrame():

{ 
// save the output of glReadPixels somewhere... I'm a noob about JNI however, so I leave this part as an exercise for the reader ;-)
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
checkGlError("glReadPixel(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixel)");
int end = width * height * 4 - 1;
// This function returns the same results regardless of where the data comes from (screen or fbo)
LOGI("glReadPixel => (%hhu,%hhu,%hhu,%hhu),(%hhu,%hhu,%hhu,%hhu),(%hhu,%hhu,%hhu,%hhu),..., (%hhu, %hhu, %hhu, %hhu)",
      pixel[0], pixel[1], pixel[2], pixel[3],
      pixel[4], pixel[5], pixel[6], pixel[7],
      pixel[8], pixel[9], pixel[10], pixel[11],
      pixel[end - 3], pixel[end - 2], pixel[end - 1], pixel[end]);
}

The resuls in logcat are identical, whether you draw on the framebuffer or on the screen:

I/libgl2jni( 5246): glReadPixel => (0,4,0,255),(8,4,8,255),(0,4,0,255),..., (0, 4, 0, 255)
I/libgl2jni( 5246): glReadPixel => (8,4,8,255),(8,8,8,255),(0,4,0,255),..., (8, 8, 8, 255)
I/libgl2jni( 5246): glReadPixel => (8,8,8,255),(8,12,8,255),(8,8,8,255),..., (8, 8, 8, 255)
I/libgl2jni( 5246): glReadPixel => (8,12,8,255),(16,12,16,255),(8,12,8,255),..., (8, 12, 8, 255)
I/libgl2jni( 5246): glReadPixel => (16,12,16,255),(16,16,16,255),(8,12,8,255),..., (16, 16, 16, 255)
[...]

tested on Froyo (phone) and on a 4.0.3 tablet.

You can find all other details in the original NDK example (GL2JNIActivity).

Hope this helps

EDIT: also make sure to check:

Android NDK glReadPixels() from offscreen buffer

where the poster seemed to have your same symptoms (and solved the problem calling glReadPixels from the right thread).

Community
  • 1
  • 1
Rick77
  • 3,121
  • 25
  • 43