Compute shader related crash on AMD HD6000 series cards with basic example

#1

My program crashes on AMD HD6000 series cards, both laptop and desktop, tested on Windows 10. The program is pretty simple:

[ol]
[li]Write to an image from a compute shader[/li][li]Draw a triangle to a framebuffer object[/li][li]Draw a triangle to the default framebuffer[/li][/ol]
All three passes are independent, meaning the results from one pass are not used by the other. And if any of the three passes are disabled, the program works.
When run, there are no GL warnings or errors. On frame 1 it draws the white triangle from pass 3 and stutters, then gets to frame 2, then frame 3, then hangs forever.
The code works on every NVIDIA card I’ve tried (GTX 460, GTX 1080, GTX 780TI) and also works on AMD HD7000 series cards. The computers that crash both say they have OpenGL 4.5 support. Unfortunately, they are not my computers so I can’t go crazy installing development software on them. I’m running RenderDoc on the working computers and everything seems correct.

The code is only 140 lines, and over half is boilerplate (syntax highlighted version here: https://hastebin.com/kakoranuta.cpp)


#include <glad/glad.c>
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <assert.h>

static void glfwErrorCallback(int error, const char* description)
{
    printf("GLFW: %s
", description);
}
static void glErrorCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
{
    printf("GL: %s
", message);
}

struct ShaderData { GLenum type; const char* source; };
GLuint BuildProgram(const ShaderData* datas, const uint32_t count)
{
    GLuint program = glCreateProgram();
    for (uint32_t i = 0; i < count; i++)
    {
        GLuint shader = glCreateShader(datas[i].type);
        glShaderSource(shader, 1, &datas[i].source, NULL);
        glCompileShader(shader); 
        
        GLint compileStatus;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus);
        if (compileStatus == GL_FALSE)
        {
            const uint32_t kInfoLogLengthMax = 1024;
            char infoLog[kInfoLogLengthMax];
            glGetShaderInfoLog(shader, kInfoLogLengthMax, 0, infoLog);
            printf("GL: Shader compilation failure - %s
", infoLog);
            assert(false);
        }

        glAttachShader(program, shader);
    }

    glLinkProgram(program);

    GLint linkStatus;
    glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
    if (linkStatus == GL_FALSE)
    {
        const uint32_t kInfoLogLengthMax = 1024;
        char infoLog[kInfoLogLengthMax];
        glGetProgramInfoLog(program, kInfoLogLengthMax, 0, infoLog);
        printf("GL: Program compilation failure - %s
", infoLog);
        assert(false);
    }

    return program;
}

int main(void)
{
    GLFWwindow* window;
    glfwSetErrorCallback(glfwErrorCallback);
    glfwInit();
    const uint32_t windowWidth = 640;
    const uint32_t windowHeight = 480;
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
    window = glfwCreateWindow(windowWidth, windowHeight, "HelloGL", NULL, NULL);
    glfwMakeContextCurrent(window);
    gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
    glfwSwapInterval(1);

    // Other state
    glEnable(GL_DEBUG_OUTPUT);
    glDebugMessageCallback(glErrorCallback, NULL);
    glCullFace(GL_BACK);
    glFrontFace(GL_CW);

    // Compute
    static const char* comp =
        "#version 430
"
        "layout(local_size_x = 1, local_size_y = 1) in;
"
        "layout(location = 0, rgba8) writeonly uniform image2D o_color;
"
        "void main(){imageStore(o_color, ivec2(gl_GlobalInvocationID), vec4(1.0));}
";
    ShaderData compShaders[] = { {GL_COMPUTE_SHADER, comp} };
    GLuint compProgram = BuildProgram(compShaders, 1);
    GLuint compRenderTarget; glGenTextures(1, &compRenderTarget);
    glBindTexture(GL_TEXTURE_2D, compRenderTarget);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, windowWidth, windowHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glBindImageTexture(0, compRenderTarget, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8);

    // Vert/Frag
    static const char* vert =
        "#version 330
"
        "void main(){gl_Position = vec4(vec2((gl_VertexID >> 1) & 1, gl_VertexID & 1), 0.0, 1.0);}
";
    static const char* frag =
        "#version 330
"
        "layout(location = 0) out vec4 o_color;
"
        "void main(){o_color = vec4(1.0);}
";
    ShaderData triShaders[] = { {GL_VERTEX_SHADER, vert}, {GL_FRAGMENT_SHADER, frag} };
    GLuint triProgram = BuildProgram(triShaders, 2);
    GLuint triVao; glGenVertexArrays(1, &triVao);
    GLuint triFbo; glGenFramebuffers(1, &triFbo);
    GLuint triRenderTarget; glGenTextures(1, &triRenderTarget);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, triFbo);
    glBindTexture(GL_TEXTURE_2D, triRenderTarget);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, windowWidth, windowHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, triRenderTarget, 0);
    assert(glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);

    // Comment out any of the lines ending with // HERE to make the program not crash on HD6000 series cards
    uint32_t frameCount = 0;
    while (!glfwWindowShouldClose(window))
    {
        // Comp
        glUseProgram(compProgram);
        glDispatchCompute(windowWidth, windowHeight, 1); // HERE

        // Tri
        glViewport(0, 0, windowWidth, windowHeight);
        glUseProgram(triProgram);
        glBindVertexArray(triVao);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, triFbo);
        glDrawArrays(GL_TRIANGLES, 0, 3); // HERE
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glDrawArrays(GL_TRIANGLES, 0, 3); // HERE

        glfwSwapBuffers(window);
        glfwPollEvents();

        assert(glGetError() == GL_NO_ERROR);
        printf("%d
", frameCount++);
    }

    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;
}

#2

OK I had to put a glClear(GL_COLOR_BUFFER_BIT); after glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

Seems like a driver bug… but glad there’s a workaround