Search code examples
c++openglgraphicsopengl-esgpu

Hot to Fill Output Buffer in OpenGL Compute Shaders


I want to calculate some values in my compute shader and return them to a heap allocated buffer on CPU side.

I send 5 float numbers in an array named as "input_buffer" to GPU with my compute shader and I want to multiply all values by 3 and 6 in that array.

The problem is I can't fill my output buffer even if I bind it correct corresponding binding index in my Compute Shader code.

This is my code -->

const char* compute_shader = "\n" \
        "#version 430 core\n" \
        "layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n" \
        "layout(std430, binding = 0) buffer input_layout\n" \
        "{\n" \
        "   float Elements[];\n" \
        "};\n" \
        "layout(std430, binding = 1) buffer output_layout\n" \
        "{\n" \
        "   float Elements2[];\n" \
        "};\n" \
        "void main()\n" \
        "{\n" \
        "   const uint idx = gl_GlobalInvocationID.x;\n" \
        "   Elements2[idx] = Elements[idx] * 3.0f;\n" \
        "   Elements[idx] = Elements[idx] * 6.0f;\n" \
        "}\n" \
        "\n";

    uint32_t shader = CreateShader(compute_shader);
    glUseProgram(shader);

    //int len = width * height;
    int len = 5;
    float* input_buffer = new float[len];
    float* output_buffer = new float[len];

    for (size_t idx = 0; idx < len; idx++)
    {
        input_buffer[idx] = 65.0f;
        output_buffer[idx] = -1001.0f;
    }

    GLuint SSBO;  //Shader Storage Buffer Object
    glGenBuffers(1, &SSBO);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO);
    glBufferData(GL_SHADER_STORAGE_BUFFER, len * sizeof(float), (GLvoid*)input_buffer, GL_DYNAMIC_COPY);
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, SSBO);

    GLuint SSBO2;
    glGenBuffers(1, &SSBO2);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO2);
    glBufferData(GL_SHADER_STORAGE_BUFFER, len * sizeof(float), (GLvoid*)output_buffer, GL_DYNAMIC_COPY);
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, SSBO2);

    glUseProgram(shader);

    glDispatchCompute(len, 1, 1);
    glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT | GL_BUFFER_UPDATE_BARRIER_BIT);

    float* dummy_out = nullptr;
    dummy_out = (float*)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_WRITE);
    
    printf("\nInput Buffer: ");
    for (int idx = 0; idx < len; idx++)
    {
        printf("%f ", input_buffer[idx]);
    }
    printf("\n");

    printf("\nOutput Buffer: ");
    for (int idx = 0; idx < len; idx++)
    {
        printf("%f ", output_buffer[idx]);
    }
    printf("\n");

    if (nullptr != dummy_out)
    {
        printf("\nDummy_Output: ");
        for (int idx = 0; idx < len; idx++)
        {
            printf("%f ", dummy_out[idx]);
        }
        printf("\n");
    }

    glDeleteProgram(shader);
    delete[] input_buffer, output_buffer, dummy_out;

And the output is -->

OpenGL Version: 4.3.0 - Build 31.0.101.2114

Input Buffer: 50.000000 50.000000 50.000000 50.000000 50.000000

Output Buffer: -1001.000000 -1001.000000 -1001.000000 -1001.000000 -1001.000000

Dummy_Output: 150.000000 150.000000 150.000000 150.000000 150.000000

Why I can't see the changes in my buffer named as "output_buffer"? Is there any way to fill it on specially? This is what I need.

I tried other GLenum flags to get data.


Solution

  • It seems you misunderstood how buffers in OpenGL work. Buffers contain a copy of the array passed to glBufferData, they do not establish any connection with between the buffer and the array. So when the buffer is changed, these changes are not reflected in the original array.

    If you want to consume GPU generated data on the CPU side, you have to read the buffer content back with glGetBufferSubData, for example:

    glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO2);
    glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, len * sizeof(float), (GLvoid*)output_buffer);
    

    Please also note, that you probably need to add a memory barrier after the shader execution to make sure that the GPU has finished before reading the content back.