Search code examples
androidc++opengl-esglslshader

Minimal working example of compute shader for Open GL ES 3.1


I want to use Open GL ES for general purpose computing.

So the thing I understood so far is that I need to create a SSBO and transfer my data there, bind the buffers to special points in shaders, run the shaders and get data back.

I have 3 problems so far:

  1. The code doesn't compile: error: undefined reference to 'glDispatchCompute'. I have included GLES3/gl31.h, and the function is there. Other functions from open gl es are imported correctly;
  2. I don't understand how to get data back from the buffer;
  3. There can be some other mistakes in the code. I haven't found an example of Open GL ES 3.1 compute shaders code on the web, so I tried to combine usual Open GL ES code (and I am novice in that) and compute shaders code from the link above.

Also I am writing this code to be launched from Android app, so some problems can appear from that side. My end task is to calculate something on that shaders and return to Android app. Now it only returns fixed string I used for debug.

Here is my code:

#include <jni.h>
#include <string>


#include <GLES3/gl31.h>
//#include <GLES/egl.h>


static const char COMPUTE_SHADER[] =
        "#version 310 es\n"
                "layout(local_size_x = 128) in;\n"
                "layout(std430) buffer;\n"
                "layout(binding = 0) writeonly buffer Output {\n"
                "vec4 elements[];\n"
                "} output_data;\n"
                "layout(binding = 1) readonly buffer Input0 {\n"
                "vec4 elements[];\n"
                "} input_data0;\n"
                "void main()\n"
                "{\n"
                "    uint ident = gl_GlobalInvocationID.x;\n"
                "output_data.elements[ident] = input_data0.elements[ident] * input_data0.elements[ident];\n"
                "}";

GLuint LoadShader(const char *shaderSrc)
{

    GLuint shader;
    GLint compiled;

    // Create the shader object
    shader = glCreateShader(GL_COMPUTE_SHADER);
    if(shader == 0)
        return shader;

    // Load the shader source
    glShaderSource(shader, 1, &shaderSrc, NULL);

    // Compile the shader
    glCompileShader(shader);
    // Check the compile status
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    if(!compiled)
    {
        GLint infoLen = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);

        if(infoLen > 1)
        {
            char* infoLog = (char*)malloc(sizeof(char) * infoLen);
            glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
            //esLogMessage("Error compiling shader:\n%s\n", infoLog);
            free(infoLog);
        }
        glDeleteShader(shader);
        return 0;
    }
    return shader;
}

extern "C" JNIEXPORT jstring

JNICALL
Java_appname_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    // Maybe create a shader straight here
    //prepare_data();
    //GLuint tex[2];
    char hello[100] = "hello";

    GLuint data_buffer;
    GLuint output_buffer;

    uint32_t data[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    glGenBuffers(1, &data_buffer);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, data_buffer);
    glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(uint32_t) * 10, (void*)data, GL_STREAM_COPY);
    glBindBufferBase( GL_SHADER_STORAGE_BUFFER, 1, data_buffer);

    glGenBuffers(0, &output_buffer);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, output_buffer);
    //glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(uint32_t) * 10, (void*)calc_data, GL_STREAM_COPY);
    glBindBufferBase( GL_SHADER_STORAGE_BUFFER, 0, output_buffer);

    GLuint program = glCreateProgram();
    GLuint shader = LoadShader(COMPUTE_SHADER);

    glAttachShader(program, shader);
    glLinkProgram(program);

    glUseProgram(program);
    glDispatchCompute(10,1,1);

    GLuint *ptr = (GLuint *) glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 10, GL_READ_ONLY );
    GLuint info = ptr[ 0 ];
    glUnmapBuffer( GL_SHADER_STORAGE_BUFFER );
    sprintf(hello, "%d ", info);

    glMemoryBarrier( GL_SHADER_STORAGE_BARRIER_BIT );

    return env->NewStringUTF(hello);
}

Solution

  • The first issue is, that you have to use the proper data type (uint) in the specification of the buffer objects:

    layout(binding = 0) writeonly buffer Output
    {
        uint elements[];
    } output_data;
    
    layout(binding = 1) readonly buffer Input0
    {
        uint elements[];
    } input_data0;
    

    Further you have creates and initializes a buffer object's data store by glBufferData:

    glGenBuffers(0, &output_buffer);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, output_buffer);
    glBindBufferBase( GL_SHADER_STORAGE_BUFFER, 0, output_buffer);
    
    glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(GLuint) * 10, nullptr, GL_DYNAMIC_READ);
    

    When you do so, then the mapping of the buffer by glMapBufferRange will work, if you use the GL_MAP_READ_BIT (instead of the enum constant GL_READ_ONLY, which makes no sense in this case at all):

    GLuint *ptr = (GLuint*)glMapBufferRange(
        GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLuint) * 10, GL_MAP_READ_BIT );