user3537499
user3537499

Reputation: 61

Android NDK OpenGLES not rendering triangles

I have been struggling to correctly render any geometry using OpenGLES 1.0, 2.0, or 3.0 techniques. My testing device is a Samsung Galaxy S7 Edge (running Android 7.0). I have implemented numerous guides for both OpenGLES and EGL such as:

https://www.khronos.org/registry/EGL/sdk/docs/man/html/

https://www.khronos.org/assets/uploads/books/openglr_es_20_programming_guide_sample.pdf

http://www.ikerhurtado.com/egl-use-android-native-opengles-applications

The code was initially adapted from the "native-activity" example:

https://github.com/googlesamples/android-ndk/tree/master/native-activity


Android Manifest: (mostly from native-activity example)

...
<!-- Tell the system this app requires OpenGL ES 3.0. -->
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
...

CMAKE: (mostly from native-activity example)

...

add_library(
    native-activity
    SHARED
    main.cpp)

target_include_directories(
    native-activity
    PRIVATE
    ${ANDROID_NDK}/sources/android/native_app_glue)

# add lib dependencies
target_link_libraries(
    native-activity
    android
    native_app_glue
    EGL
    GLESv3
    log)

...

main.cpp: (mostly from native-activity example, cropped for relevance)

...

#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES3/gl3.h>
#include <GLES3/gl3ext.h>
#include <android_native_app_glue.h>

...

/*
 *Test GLES 1.0, should draw a white triangle on red background
 */
class TestGLES10 {
public:
    GLuint program;

    void create() {

        char vssource[] =
                "attribute vec4 vertexPosition; \n"
                "void main(){                       \n"
                "   gl_Position = vertexPosition;   \n"
                "}                                  \n";

        char fssource[] =
                "precision mediump float; \n"
                "void main(){ \n"
                "   gl_FragColor = vec4(1.0); "
                "}                                  \n";

        auto compile = [](const char *source, GLenum type){
            GLuint shader = glCreateShader(type);
            glShaderSource(shader, 1, &source, nullptr);
            glCompileShader(shader);
            return shader;
        };

        GLuint vs = compile(string(vssource).c_str(), GL_VERTEX_SHADER);
        GLuint fs = compile(string(fssource).c_str(), GL_FRAGMENT_SHADER);

        program = glCreateProgram();
        glAttachShader(program, vs);
        glAttachShader(program, fs);

        glBindAttribLocation(program, 0, "vertexPosition");

        glLinkProgram(program);

        glDisable(GL_DEPTH_TEST);
    }

    void update() {

        glClearColor(1,0,0,1);
        glClear(GL_COLOR_BUFFER_BIT);

        glUseProgram(program);

        float positions[]{
                0.0f,  0.1f,
                -0.1f, -0.1f,
                0.1f, -0.1f
        };

        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, positions);
        glEnableVertexAttribArray(0);

        glDrawArrays(GL_TRIANGLES, 0, 3);
    }
};

/*
 *Test GLES 3.0, should draw a white triangle on blue background
 */
class TestGLES30 {
public:
    GLuint vbo, vao, program;

    void create() {

        char vssource[] =
            "#version 300 es\n"
            "layout(location=0) in vec4 vertexPosition; \n"
            "void main(){                       \n"
            "   gl_Position = vertexPosition;   \n"
            "}                                  \n";

        char fssource[] =
            "#version 300 es\n"
            "precision mediump float; \n"
            "out vec4 fragment; \n"
            "void main(){ \n"
            "   fragment = vec4(1.0); "
            "}                                  \n";

        auto compile = [](const char *source, GLenum type){
            GLuint shader = glCreateShader(type);
            glShaderSource(shader, 1, &source, nullptr);
            glCompileShader(shader);
            return shader;
        };

        GLuint vs = compile(string(vssource).c_str(), GL_VERTEX_SHADER);
        GLuint fs = compile(string(fssource).c_str(), GL_FRAGMENT_SHADER);

        program = glCreateProgram();
        glAttachShader(program, vs);
        glAttachShader(program, fs);
        glLinkProgram(program);

        float positions[]{
             0.0f,  0.5f,
            -0.5f, -0.5f,
             0.5f, -0.5f
        };

        glGenBuffers(1, &vbo);
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glBufferData(GL_ARRAY_BUFFER, 2*4, positions, GL_STATIC_DRAW);

        glGenVertexArrays(1, &vao);
        glBindVertexArray(vao);

        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2*4, 0);

        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindVertexArray(0);
    }

    void update() {

        glClearColor(0,0,1,1);
        glClear(GL_COLOR_BUFFER_BIT);

        glUseProgram(program);

        glBindVertexArray(vao);
        glDrawArrays(GL_TRIANGLES, 0, 3);
        glBindVertexArray(0);
    }
};

...

void android_main(android_app* appInterface) {

    appInterface->onAppCmd = commandRelay;
    appInterface->onInputEvent = inputRelay;

    while(appInterface->window == nullptr){
        pollEvents(appInterface);
    }

    EGLDisplay display;
    EGLSurface surface;
    EGLContext context;
    EGLint majorVersion, minorVersion;
    EGLConfig config;
    EGLint width, height;
    EGLint nativeVisualID;
    EGLint configCount;

    display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    assert(display != EGL_NO_DISPLAY);

    eglInitialize(display, &majorVersion, &minorVersion);

    //https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglChooseConfig.xhtml
    const EGLint displayAttrib[] = {
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,//egl 1.3 req
        EGL_NATIVE_RENDERABLE, EGL_TRUE,
        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
        EGL_BLUE_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_RED_SIZE, 8,
        EGL_NONE
    };

    //retreive configs
    eglChooseConfig(display, displayAttrib, 0, 0, &configCount);
    std::unique_ptr<EGLConfig[]> supported(new EGLConfig[configCount]);
    eglChooseConfig(display, displayAttrib, supported.get(), configCount, &configCount);

    EGLint i = 0, v;
    for (; i < configCount; i++) {

        auto get = [&](EGLint attrib){
            eglGetConfigAttrib(display, supported[i], attrib, &v);
            return v;
        };
        if (Math2::equal(8,
            get(EGL_RED_SIZE),
            get(EGL_GREEN_SIZE),
            get(EGL_BLUE_SIZE),
            get(EGL_DEPTH_SIZE))) {//expands to 8 == get(EGL_RED_SIZE) == ...

            config = supported[i];
            break;
        }
    }
    if (i == configCount) {//default to first
        config = supported[0];
    }

    const EGLint surfaceAttrib[] = {
        EGL_RENDER_BUFFER, EGL_BACK_BUFFER,// render to the back buffer, egl 1.2 req
        EGL_NONE
    };

    eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &nativeVisualID);
    surface = eglCreateWindowSurface(display, config, appInterface->window, surfaceAttrib);

    const EGLint contextAttrib[] = {
        EGL_CONTEXT_CLIENT_VERSION, 2,// request a context using Open GL ES 2.0
        EGL_NONE
    };

    context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttrib);
    assert(context != EGL_NO_CONTEXT);

    //sets initial viewport and scissor dimensions to draw surface size
    eglMakeCurrent(display, surface, surface, context);

    eglQuerySurface(display, surface, EGL_WIDTH, &width);
    eglQuerySurface(display, surface, EGL_HEIGHT, &height);

    //this call was mentioned, but missing from the example (removing has same effect)
    ANativeWindow_setBuffersGeometry(appInterface->window, width, height, nativeVisualID);


    TestGLES10 test;
    test.create();

    while (true){
        test.update();
        eglSwapBuffers(display, surface);
    }
}

Results:

Logged Info:

Vendor : Qualcomm
Renderer : Adreno (TM) 530
Version : OpenGL ES 3.2 [email protected] (GIT@I86b60582e4)
EGL version : 1.4
Window dimensions : 1080, 1920

Additionally, the device has resolution scaling set to 1080x1920.

Test 1.0: https://i.sstatic.net/gEgai.png Test 3.0: https://i.sstatic.net/Bw27a.png

According to: https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglMakeCurrent.xhtml the glViewPort should have been initialized to the surface size (1080x1920), so I am at a loss for how the white rectangle could occur.

The shader code for both tests compile without errors from Adreno.


Why wont it render geometry correctly?

According to : https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglCreateContext.xhtml there is no way to specifically request a 3.0 or higher context, why does the created context have version 3.2 when I can only request 2.0?

Upvotes: 2

Views: 796

Answers (1)

solidpixel
solidpixel

Reputation: 12109

Why wont it render geometry correctly?

Your position arrays are three vertices of two compoenents each (x, y), but you're telling GL they have three components each:

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, positions);

why does the created context have version 3.2 when I can only request 2.0?

Versions 3.x are entirely forward compatible with code written for version 2.0, so it's legal for the drivers just to up-convert a context to the newest version they support.

Upvotes: 1

Related Questions