Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android NDK OpenGL ES 2 Context Initialization

Trying to build a native android application using android-ndk-r8e.

Code compiles fine and runs without issues if building as Java + NDK app, that is with a Java interface that loads an .so file, initializes OpenGL and calls the methods in the .so.

However, when compiled as a "native-activity", after setup() function (code below), LogCat outputs A//system/bin/app_process(27426): stack corruption detected: aborted

void Canvas::Setup ( void ) 
{ 

    // initialize OpenGL ES 2

    // Here specify the attributes of the desired configuration.
    // Below, we select an EGLConfig with at least 8 bits per color
    // component compatible with on-screen windows     
    const EGLint attribs[] =
    {
        EGL_RENDERABLE_TYPE,    EGL_OPENGL_ES2_BIT,
        EGL_RED_SIZE,           8,
        EGL_GREEN_SIZE,         8,
        EGL_BLUE_SIZE,          8,
        EGL_NONE
    };
    // ..

    // surface, window and context related data
    EGLint      w, 
                h, 
                dummy, 
                format;

    EGLConfig   config;
    EGLSurface  surface;
    EGLContext  context;
    EGLDisplay  display = eglGetDisplay( EGL_DEFAULT_DISPLAY );
    // ..

    eglInitialize(display, 0, 0);

    // get the number of matching EGL configurations
    int num_config[1];

    eglChooseConfig(display, attribs, NULL, 1, num_config);

    const int numConfigs = num_config[0];

    if (numConfigs <= 0)
    {
        //throw new IllegalArgumentException("No configs match configSpec");
    }

    // allocate then read the array of minimally matching EGL configs
    EGLConfig configs[numConfigs];
    EGLConfig current;

    eglChooseConfig(display, attribs, configs, numConfigs, num_config);
    int i = 0;
    for(; i < numConfigs; ++i)
    {
        int d = 2, s = 2, r, g, b, a;

        eglGetConfigAttrib(display, configs[i], EGL_DEPTH_SIZE, &d);
        eglGetConfigAttrib(display, configs[i], EGL_STENCIL_SIZE, &s);

        // we need at least mDepthSize and mStencilSize bits
        if (d < 1 || s < 0)
        {
            continue;
        }

        // we want an *exact* match for red/green/blue/alpha
        eglGetConfigAttrib(display, configs[i], EGL_RED_SIZE, &r);
        eglGetConfigAttrib(display, configs[i], EGL_GREEN_SIZE, &g);
        eglGetConfigAttrib(display, configs[i], EGL_BLUE_SIZE, &b);
        eglGetConfigAttrib(display, configs[i], EGL_ALPHA_SIZE, &a);

        if (r == 8 && g == 8 && b == 8 && a == 8)
        {
                        // found it, store in i
            break;
        }
    }

    surface = eglCreateWindowSurface(display, configs[i], Canvas::Engine.app->window, NULL);

    int attrib_list[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };

    context = eglCreateContext(display, configs[i], EGL_NO_CONTEXT, attrib_list);


    if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
    {
        LOG_PRINT_ERROR("Unable to eglMakeCurrent");
    }   

    Canvas::Engine.display = display;
    Canvas::Engine.context = context;
    Canvas::Engine.surface = surface;
    Canvas::Engine.animating = true;

    eglQuerySurface(display, surface, EGL_WIDTH, &w);
    eglQuerySurface(display, surface, EGL_HEIGHT, &h);

    Canvas::Width = w;
    Canvas::Width = h;
}

Below is android_main

void android_main(struct android_app* state) 
{

    // make sure glue isn't stripped.
    app_dummy();
    // ..

    // hook to events
    memset(&Canvas::Engine, 0, sizeof(Canvas::Engine));
    state->userData = &Canvas::Engine;
    state->onAppCmd = Canvas::HandleCommand;
    state->onInputEvent = Canvas::HandleInput;
    Canvas::Engine.app = state;
    // ..


    // loop waiting for stuff to do
    while (1) 
    {
        // read all pending events.
        int ident;
        int events;
        struct android_poll_source* source;

        // If not animating, we will block forever waiting for events.
        // If animating, we loop until all events are read, then continue
        // to draw the next frame of animation.
        while ((ident=ALooper_pollAll(Canvas::Engine.animating ? 0 : -1, NULL, &events, (void**)&source)) >= 0)
        {
            // process this event.
            if (source != NULL)
            {
                source->process(state, source);
            }

            // check if we are exiting.
            if (state->destroyRequested != 0)
            {
                Canvas::Cleanup();
                return;
            }
        }

        Canvas::Render();
    }
    // ..
}

Here is where I handle Android window commands.

void Canvas::HandleCommand(struct android_app* app, int32_t cmd)

{

    switch (cmd)
    {

        case APP_CMD_INIT_WINDOW:
            // window is being shown, get it ready

                    LOG_PRINT_INFO("before setup");
            Canvas::Setup();
                    LOG_PRINT_INFO("after setup");

                    LOG_PRINT_INFO("before resize);
            Canvas::Resize(Canvas::Engine.width, Canvas::Engine.height);
                    LOG_PRINT_INFO("after resize);
        break;

        case APP_CMD_TERM_WINDOW:
            // window is being hidden or closed, clean it up
            Canvas::Cleanup();
        break;
    }
}

LogCat prints correctly the message "before setup". If you look at the code, it should print "after setup". Instead it prints "stack corruption detected: aborted" sometimes. Other times it just plain exits the loop, even though there's no code (that I've written) to make it return like that.

I should mention that the NDK code is based on this sample. The Java version pretty much looks the same and runs fine.

like image 871
Bebe Carabina Avatar asked May 30 '13 21:05

Bebe Carabina


1 Answers

I'm pretty sure it's because of this line

surface = eglCreateWindowSurface(display, configs[i], Canvas::Engine.app->window, NULL);

Your more or less hypothetical problem there is that in your for loop from 0 to numConfigs you look for a configuration that might not be available in your emulator or device, so if your code never reaches that break, then you create your surface and context based on data outside of your array (because i will be numConfigs in this case), which in turn would explain the erratic behavior.

like image 114
Radu Chivu Avatar answered Oct 01 '22 23:10

Radu Chivu