c++openglglslcompute-shadershader-storage-buffer

My Compute Shader doesn't have any outside effects


I'm trying to write a compute shader in GLSL with OpenGL, GLFW, GLEW, and GLM. The problem is that the compute shader either isn't running or isn't modifying any buffers. I have the latest NVIDIA 4060 Ti Drivers and I'm working on WSL on Windows with Mesa.

Code (this and glew.c (I know, hacky) goes in src directory, glm should go in include):

#include <iostream>
#include <cstring>
#include <GLFW/glfw3.h>
#include "glm.hpp"
#include <GL/gl.h>

int main(int argc, char **argv) {
    //Window width and height
    int window_w = 640, window_h = 480;

    printf("Startup\n");

    //Init and create window
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, false);
    glfwWindowHint(GLFW_SAMPLES, 4);
    glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true);
    std::cout << "Trying to create window" << std::endl;
    GLFWwindow* window = glfwCreateWindow(window_w, window_h, "Marching Cubes", nullptr, nullptr);
    std::cout << "Just tried to create window\n" << std::endl;

    if (window == nullptr)
    {
        std::cerr << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        exit(EXIT_FAILURE);
    }
    else {
        std::cerr << "Succeeded at creating window" << std::endl;
    }

    //More init
    glfwMakeContextCurrent(window);
    printf("Made the window the current context\n");

    glEnable              ( GL_DEBUG_OUTPUT );

    glewInit();

    // The buffer for use in the compute shader
    uint32_t buffer_vertices;
    uint32_t buffer_size = 10;

    glCreateBuffers(1, &buffer_vertices);
    glNamedBufferStorage(buffer_vertices, buffer_size, nullptr, GL_DYNAMIC_STORAGE_BIT);

    printf("Buffers have been created\n");

    // Create the VAO for drawing the resulting triangles
    uint32_t vao_draw;
    glCreateVertexArrays(1, &vao_draw);

    glEnableVertexArrayAttrib(vao_draw, 0);
    glEnableVertexArrayAttrib(vao_draw, 1);

    glVertexArrayVertexBuffer(vao_draw, 0, buffer_vertices, 0, sizeof(glm::vec4));
      
    glVertexArrayAttribFormat(vao_draw, 0, 4, GL_FLOAT, false, 0);
    glVertexArrayAttribFormat(vao_draw, 1, 4, GL_FLOAT, false, 0);
       
    glVertexArrayAttribBinding(vao_draw, 0, 0);
    glVertexArrayAttribBinding(vao_draw, 1, 1);

    std::cout << "GLSL Version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;

    glClearColor(1.0, 1.0, 1.0, 0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glBegin(GL_TRIANGLES);

    // The compute shader
    char* smcsource = "\n\
#version 430\n\
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\
layout(std430, binding = 0) buffer u_buffer_vertices\n\
{\n\
    vec4 output_vertices[];\n\
};\n\
\n\
void main() {\n\
    for (float i = 0.0; i < 10.0; i++) output_vertices[int(i)] = vec4(i,i,i,i);\n\
}\n\
";

    //Compile the shader
    GLint length = strlen(smcsource);
    GLuint compute_shader = glCreateShader(GL_COMPUTE_SHADER);
    glShaderSource(compute_shader, 1, &smcsource, &length);
    glCompileShader(compute_shader);
    GLint smcsuccess = 0;
    glGetShaderiv(compute_shader, GL_COMPILE_STATUS, &smcsuccess);
    GLuint smcprogram;
    if (smcsuccess) {
        printf("Successfully compiled\n");
        smcprogram = glCreateProgram();
        glAttachShader(smcprogram, compute_shader);
        glLinkProgram(smcprogram);
        GLint TotalLength = 0;
        glGetShaderiv(compute_shader, GL_INFO_LOG_LENGTH, &TotalLength);
        std::string logs;
        logs.resize(TotalLength);
        glGetShaderInfoLog(compute_shader, TotalLength, NULL, &logs[0]);
        std::cout << "Shader output log:\n" << logs << std::endl;
    }
    else {
        printf("Failed compile\n");
        GLint TotalLength = 0;
        glGetShaderiv(compute_shader, GL_INFO_LOG_LENGTH, &TotalLength);
        std::string logs;
        logs.resize(TotalLength);
        glGetShaderInfoLog(compute_shader, TotalLength, NULL, &logs[0]);
        std::cout << "Shader output log:\n" << logs << std::endl;
    }

    //Select shader
    glUseProgram(smcprogram);

    //Bind buffer
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffer_vertices);
    printf("Buffer binded\n");

    //Run shader. Problem happens here: nothing happens to the buffer when I run this
    glDispatchCompute(1, 1, 1);
    glMemoryBarrier(GL_ALL_BARRIER_BITS);

    //Test and print the buffer data
    //If this is working as intended, it should count up in groups of four, something like:
    //0.0 0.0 0.0 0.0 | 1.0 1.0 1.0 1.0 | 2.0 2.0 2.0 2.0 | 3.0 3.0 3.0 3.0 | etc.
    glm::vec4 vertices[16];
    glGetBufferSubData(buffer_vertices, 0, 16, &vertices);
    printf("Vertex data after: ");
    for (int i = 0; i < 10; i++)
        printf("%f %f %f %f | ", vertices[i][0], vertices[i][1], vertices[i][2], vertices[i][3]);
    printf("\n");

    glEnd();
    glfwSwapBuffers(window);

    return 0;
}

Can be compiled with:

g++ src/main.cpp src/glew.c -Iinclude -lGLEW -lglut -lglfw -lGL -lX11 -lc -o shader-compiler-bug

Like the comments say, based on the compute shader it should (along with the debugging messages) print:

0.0 0.0 0.0 0.0 | 1.0 1.0 1.0 1.0 | 2.0 2.0 2.0 2.0 | 3.0 3.0 3.0 3.0 | [etc. to 10]

Instead, that part of it prints:

-11653242706460672.000000 0.000000 0.000000 0.000000 | -496068722688.000000 0.000000 -496183017472.000000 0.000000 | -496015245312.000000 0.000000 -496055091200.000000 0.000000 | -496050896896.000000 0.000000 -496059285504.000000 0.000000 | -496066625536.000000 0.000000 -447767117824.000000 0.000000 | 0.000000 0.000000 0.000000 -0.000000 | 0.000000 0.000000 0.000000 0.000000 | 0.000000 0.000000 0.000000 0.000000 | 0.000000 0.000000 0.000000 0.000000 | -496055091200.000000 0.000000 -496025731072.000000 0.000000 |

(It's different every time though, some attempts are full of zeroes or have NaNs in the output)


Solution

  • A few issues:

    All together (apologies for the GLAD conversion, it's what I had handy):

    #include <glad/glad.h>
    #define GLFW_INCLUDE_NONE
    #include <GLFW/glfw3.h>
    #include <glm/glm.hpp>
    #include <vector>
    #include <iostream>
    
    void CheckStatus( GLuint obj, bool isShader )
    {
        GLint status = GL_FALSE, log[ 1 << 11 ] = { 0 };
        ( isShader ? glGetShaderiv : glGetProgramiv )( obj, isShader ? GL_COMPILE_STATUS : GL_LINK_STATUS, &status );
        if( status == GL_TRUE ) return;
        ( isShader ? glGetShaderInfoLog : glGetProgramInfoLog )( obj, sizeof( log ), NULL, (GLchar*)log );
        std::cerr << (GLchar*)log << "\n";
        std::exit( EXIT_FAILURE );
    }
    
    void AttachShader( GLuint program, GLenum type, const char* src )
    {
        GLuint shader = glCreateShader( type );
        glShaderSource( shader, 1, &src, NULL );
        glCompileShader( shader );
        CheckStatus( shader, true );
        glAttachShader( program, shader );
        glDeleteShader( shader );
    }
    
    const char* const comp = R"GLSL(
    #version 430
    layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
    layout(std430, binding = 0) buffer u_buffer_vertices
    {
        vec4 output_vertices[];
    };
    
    void main() {
        for (float i = 0.0; i < 10.0; i++) {
            output_vertices[int(i)] = vec4(i,i,i,i);
        }
    }
    )GLSL";
    
    int main( int, char** )
    {
        glfwSetErrorCallback( []( int, const char* desc ) { std::cerr << desc << "\n"; std::exit( EXIT_FAILURE ); } );
        glfwInit();
        glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 4 );
        glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 6 );
        glfwWindowHint( GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE );
        glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE );
        GLFWwindow* window = glfwCreateWindow( 640, 480, "GLFW", NULL, NULL );
        glfwMakeContextCurrent( window );
        gladLoadGLLoader( (GLADloadproc)glfwGetProcAddress );
    
        GLuint prog = glCreateProgram();
        AttachShader( prog, GL_COMPUTE_SHADER, comp );
        glLinkProgram( prog );
        CheckStatus( prog, false );
        glUseProgram( prog );
    
        // The buffer for use in the compute shader
        uint32_t buffer_vertices;
        uint32_t buffer_size = 10;
        glCreateBuffers( 1, &buffer_vertices );
        glNamedBufferStorage( buffer_vertices, buffer_size * sizeof(glm::vec4), nullptr, GL_DYNAMIC_STORAGE_BIT );
    
        //Bind buffer
        glBindBufferBase( GL_SHADER_STORAGE_BUFFER, 0, buffer_vertices );
    
        //Run shader.
        glDispatchCompute( 1, 1, 1 );
        glMemoryBarrier( GL_ALL_BARRIER_BITS );
    
        glm::vec4 vertices[10];
        glGetNamedBufferSubData( buffer_vertices, 0, buffer_size * sizeof(glm::vec4), &vertices );
    
        printf("Vertex data after: \n");
        for( int i = 0; i < 10; i++ )
        {
            printf( "%f %f %f %f \n", vertices[i][0], vertices[i][1], vertices[i][2], vertices[i][3] );
        }
        printf( "\n" );
    
        glfwTerminate();
        return 0;
    }
    

    Output:

    Vertex data after:
    0.000000 0.000000 0.000000 0.000000
    1.000000 1.000000 1.000000 1.000000
    2.000000 2.000000 2.000000 2.000000
    3.000000 3.000000 3.000000 3.000000
    4.000000 4.000000 4.000000 4.000000
    5.000000 5.000000 5.000000 5.000000
    6.000000 6.000000 6.000000 6.000000
    7.000000 7.000000 7.000000 7.000000
    8.000000 8.000000 8.000000 8.000000
    9.000000 9.000000 9.000000 9.000000