Search code examples
c++macosopenglglfwvao

MacOS runtime error : Validation failed : No VAO bound


I need help with the setup of an OpenGL project in CLion on MacOS. The project also makes use of glad and GLFW. On top of that I included some header files and source files into the project that are supposed to setup a shader etc. . When I run the project, the build is successful but I immediately get this runtime error:

libc++abi: terminating with uncaught exception of type std::runtime_error: Validation Failed: No vertex array object bound.

Process finished with exit code 134 (interrupted by signal 6: SIGABRT)

I will provide the code of the source and corresponding header file that I believe to be most likely responsible :

ShaderInterface.cpp:

#include <fstream>
#include <streambuf>
#include <iostream>
#include "ShaderInterface.h"
#include "StringHelper.h"

int ShaderInterface::lastShaderUsed = -1;
int ShaderInterface::lastUsedShaderID = -1;



ShaderInterface::ShaderInterface(string name, bool useTexture, bool useBump, bool useBlinn, bool useColor) {
    this->useTexture = useTexture;
    this->useBlinn = useBlinn;
    this->useBump = useBump;
    this->useColor = useColor;
    this->name = name;
    uniformVars = unordered_map<string, UniformVariable*>();
    idToVarMap = unordered_map<int, UniformVariable*>();
    arrayVarSizes = unordered_map<string, int>();
    initUniformVarBuffers();
    unknownShaderVarMessages = unordered_set<string>();
    programCompilationFinished = false;
    programID = -1;
    fragmentShader = -1;
    vertexShader = -1;
}



ShaderInterface::~ShaderInterface() {
    glDetachShader(programID, vertexShader);
    glDetachShader(programID, fragmentShader);
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    glDeleteProgram(programID);
    deleteUniformVars();
}



void ShaderInterface::setPathToShaderDir(string path) {
    if (!(StringHelper::endsWith(path, "\\") || StringHelper::endsWith(path, "/")))

        path = path + "/";
    this->pathToShaderPrograms = path;
}



void ShaderInterface::registerVar(string name, UniformVariableType type) {
    if (programCompilationFinished)
        throw runtime_error("Cannot register Variable " + name + ", because the compilation of the shader-program has already finished.");
    if (uniformVars.find(name) != uniformVars.end()) {
        UniformVariable* v = uniformVars[name];
        delete v;
    }
    uniformVars[name] = new UniformVariable(name, -1, type);
}



void ShaderInterface::registerVar(string name, UniformVariableType type, int arraySize) {
    if (programCompilationFinished)
        throw runtime_error("Cannot register Variable " + name + ", because the compilation of the shader-program has already finished.");
    if (uniformVars.find(name) != uniformVars.end()) {
        UniformVariable* v = uniformVars[name];
        delete v;
    }
    uniformVars[name] = new UniformVariable(name, -1, type);
    arrayVarSizes[name] = arraySize;
}



int ShaderInterface::getGLId() {
    return programID;
}


void ShaderInterface::useMe() {
    if (lastShaderUsed != programID) {
        glUseProgram(programID);
        lastShaderUsed = programID;
    }
}

void throwInfoLog(GLint id, void (*logFunc)(GLuint, GLsizei, GLsizei*, GLchar*)) {
    GLint logSize = 0;
    glGetShaderiv(id, GL_INFO_LOG_LENGTH, &logSize);
    const int maxErrorSize = 5000;
    GLchar error[maxErrorSize];
    (*logFunc)(id, maxErrorSize, nullptr, error);
    throw  runtime_error(error);

}



void ShaderInterface::finalizeProgramInitialization() {
    GLint success;
    glLinkProgram(programID);
    glValidateProgram(programID); //maybe this one
    glGetProgramiv(programID, GL_LINK_STATUS, &success);
    if (success == GL_FALSE) {
        throwInfoLog(programID, glGetProgramInfoLog);
    }
    glGetProgramiv(programID, GL_VALIDATE_STATUS, &success);
    if (success == GL_FALSE) {
        throwInfoLog(programID, glGetProgramInfoLog);
    }
    for (auto iter = uniformVars.begin(); iter != uniformVars.end(); iter++) {
        UniformVariable* v = iter->second;
        v->id = glGetUniformLocation(programID, iter->first.c_str());
        idToVarMap[v->id] = v;
    }
    programCompilationFinished = true;
}


void ShaderInterface::logUnknownShaderVar(string msg) {
    auto t = unknownShaderVarMessages.find(msg);
    if (t == unknownShaderVarMessages.end()) {
        std::cerr << msg << std::endl;
        unknownShaderVarMessages.insert(msg);
    }
}


void ShaderInterface::setFragmentShader(string code) {
    GLint success;
    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    const char* s = code.c_str();
    glShaderSource(fragmentShader, 1, &s, nullptr);
    glCompileShader(fragmentShader);

    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (success == GL_FALSE) {
        throwInfoLog(fragmentShader, glGetShaderInfoLog);
    }

    if (programID == -1) {
        programID = glCreateProgram();
    }
    glAttachShader(programID, fragmentShader);
    if (vertexShader != -1) {
        finalizeProgramInitialization();
    }
}


void ShaderInterface::setVertexShader(string code) {
    GLint success;
    vertexShader = glCreateShader(GL_VERTEX_SHADER);

    const char* s = code.c_str();
    glShaderSource(vertexShader, 1, &s, nullptr);
    glCompileShader(vertexShader);

    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (success == GL_FALSE) {
        throwInfoLog(vertexShader, glGetShaderInfoLog);
    }

    if (programID == -1) {
        programID = glCreateProgram();
    }
    glAttachShader(programID, vertexShader);
    if (fragmentShader != -1) {
        finalizeProgramInitialization();
    }
}



int ShaderInterface::getIdForVariable(string varName) {
    return uniformVars[varName]->id;
}


void execSetUniform(int id, int value) {
    glUniform1i(id, value);
}



int ShaderInterface::checkVarExistence(string name) {
    auto v = uniformVars.find(name);
    if (v == uniformVars.end())
        throw runtime_error("Unknown variable: " + name);
    return uniformVars[name]->id;
}



void ShaderInterface::setUniform(string name, int value) {
    int id = checkVarExistence(name);
    bufferedUniformIntValues[id] = value;
}



void ShaderInterface::setUniform(string name, int len, int* value) {
    int id = checkVarExistence(name);
    auto val = bufferedUniformIntArrayValues.find(id);
    if (val != bufferedUniformIntArrayValues.end()) {
        delete val->second;
    }
    bufferedUniformIntArrayValues[id] = value;
    arrayVarSizes[name] = len;
}



void ShaderInterface::setUniform(string name, float value) {
    int id = checkVarExistence(name);
    bufferedUniformFloatValues[id] = value;
}



void ShaderInterface::setUniform(string name, int len, float* value) {
    int id = checkVarExistence(name);
    if (len == 3) {
        auto val = bufferedUniformVec3fValues.find(id);
        if (val != bufferedUniformVec3fValues.end()) {
            delete val->second;
        }
        bufferedUniformVec3fValues[id] = value;
    } else if (len == 4) {
        auto val = bufferedUniformVec4fValues.find(id);
        if (val != bufferedUniformVec4fValues.end()) {
            delete val->second;
        }
        bufferedUniformVec4fValues[id] = value;
    } else {
        auto val = bufferedUniformFloatArrayValues.find(id);
        if (val != bufferedUniformFloatArrayValues.end()) {
            delete val->second;
        }
        bufferedUniformFloatArrayValues[id] = value;
        arrayVarSizes[name] = len;
    }
}



void ShaderInterface::setUniform(string name, float v1, float v2, float v3) {
    int id = checkVarExistence(name);
    float* v = new float[3]{ v1, v2, v3 };
    auto val = bufferedUniformVec3fValues.find(id);
    if (val != bufferedUniformVec3fValues.end()) {
        delete val->second;
    }
    bufferedUniformVec3fValues[id] = v;
}



void ShaderInterface::setUniform(string name, float v1, float v2, float v3, float v4) {
    int id = checkVarExistence(name);
    float* v = new float[4]{ v1, v2, v3, v4 };
    auto val = bufferedUniformVec4fValues.find(id);
    if (val != bufferedUniformVec4fValues.end()) {
        delete val->second;
    }
    bufferedUniformVec4fValues[id] = v;
}



void ShaderInterface::setUniform(string name, double value) {
    int id = checkVarExistence(name);
    bufferedUniformDoubleValues[id] = value;
}



void ShaderInterface::setUniform(string name, int len, double* value) {
    int id = checkVarExistence(name);
    if (len == 3) {
        auto val = bufferedUniformVec3dValues.find(id);
        if (val != bufferedUniformVec3dValues.end()) {
            delete val->second;
        }
        bufferedUniformVec3dValues[id] = value;
    } else if (len == 4) {
        auto val = bufferedUniformVec4dValues.find(id);
        if (val != bufferedUniformVec4dValues.end()) {
            delete val->second;
        }
        bufferedUniformVec4dValues[id] = value;
    } else {
        auto val = bufferedUniformDoubleArrayValues.find(id);
        if (val != bufferedUniformDoubleArrayValues.end()) {
            delete val->second;
        }
        bufferedUniformDoubleArrayValues[id] = value;
        arrayVarSizes[name] = len;
    }
}



void ShaderInterface::setUniform(string name, double v1, double v2, double v3) {
    int id = checkVarExistence(name);
    double* v = new double[3]{ v1, v2, v3 };
    auto val = bufferedUniformVec3dValues.find(id);
    if (val != bufferedUniformVec3dValues.end()) {
        delete val->second;
    }
    bufferedUniformVec3dValues[id] = v;
}



void ShaderInterface::setUniform(string name, double v1, double v2, double v3, double v4) {
    int id = checkVarExistence(name);
    double* v = new double[4]{ v1, v2, v3, v4 };
    auto val = bufferedUniformVec4dValues.find(id);
    if (val != bufferedUniformVec4dValues.end()) {
        delete val->second;
    }
    bufferedUniformVec4dValues[id] = v;
}


void ShaderInterface::sendUniformValues() {
    //if (lastUsedShaderID != programID) lastUsedTextureUnits.clear();
    for (auto k = bufferedUniformIntValues.begin(); k != bufferedUniformIntValues.end(); k++) {
        int id = k->first;
        int val = k->second;
        glUniform1i(id, val);
    }
    for (auto k = bufferedUniformFloatValues.begin(); k != bufferedUniformFloatValues.end(); k++) {
        int id = k->first;
        float val = k->second;
        glUniform1f(id, val);
    }
    for (auto k = bufferedUniformDoubleValues.begin(); k != bufferedUniformDoubleValues.end(); k++) {
        int id = k->first;
        double val = k->second;
        glUniform1d(id, val);
    }
    for (auto k = bufferedUniformIntArrayValues.begin(); k != bufferedUniformIntArrayValues.end(); k++) {
        int id = k->first;
        int* val = k->second;
        int size = arrayVarSizes[idToVarMap[id]->name];
        glUniform1iv(id, size, val);
    }

    for (auto k = bufferedUniformFloatArrayValues.begin(); k != bufferedUniformFloatArrayValues.end(); k++) {
        int id = k->first;
        float* val = k->second;
        int size = arrayVarSizes[idToVarMap[id]->name];
        glUniform1fv(id, size, val);
    }
    for (auto k = bufferedUniformDoubleArrayValues.begin(); k != bufferedUniformDoubleArrayValues.end(); k++) {
        int id = k->first;
        double* val = k->second;
        int size = arrayVarSizes[idToVarMap[id]->name];
        glUniform1dv(id, size, val);
    }
    for (auto k = bufferedUniformVec3fValues.begin(); k != bufferedUniformVec3fValues.end(); k++) {
        int id = k->first;
        float* val = k->second;
        glUniform3fv(id, 1, val);
    }
    for (auto k = bufferedUniformVec3dValues.begin(); k != bufferedUniformVec3dValues.end(); k++) {
        int id = k->first;
        double* val = k->second;
        glUniform3dv(id, 1, val);
    }
    for (auto k = bufferedUniformVec4fValues.begin(); k != bufferedUniformVec4fValues.end(); k++) {
        int id = k->first;
        float* val = k->second;
        glUniform4fv(id, 1, val);
    }
    for (auto k = bufferedUniformVec4dValues.begin(); k != bufferedUniformVec4dValues.end(); k++) {
        int id = k->first;
        double* val = k->second;
        glUniform4dv(id, 1, val);
    }
    lastUsedShaderID = programID;
    clearUniformVarBuffers();
}



ShaderInterface::UniformVariable::UniformVariable(std::string name, GLint id, UniformVariableType type) {
    this->name = name;
    this->id = id;
    this->type = type;
}



void ShaderInterface::initUniformVarBuffers() {
    bufferedUniformIntValues = unordered_map<int, int>();
    bufferedUniformFloatValues = unordered_map<int, float>();
    bufferedUniformDoubleValues = unordered_map<int, double>();
    bufferedUniformFloatArrayValues = unordered_map<int, float*>();
    bufferedUniformDoubleArrayValues = unordered_map<int, double*>();
    bufferedUniformIntArrayValues = unordered_map<int, int*>();
    //unordered_map<int, TextureInterface> bufferedUniformTextureValues;
    //unordered_map<int, Matrix3f> bufferedUniformMatrix3fValues;
    //unordered_map<int, Matrix4f> bufferedUniformMatrix4fValues;
    bufferedUniformVec3fValues = unordered_map<int, float*>();
    bufferedUniformVec4fValues = unordered_map<int, float*>();
    bufferedUniformVec3dValues = unordered_map<int, double*>();
    bufferedUniformVec4dValues = unordered_map<int, double*>();
}



void ShaderInterface::clearUniformVarBuffers() {
    bufferedUniformIntValues.clear();
    bufferedUniformFloatValues.clear();
    bufferedUniformDoubleValues.clear();

    for (auto k = bufferedUniformIntArrayValues.begin(); k != bufferedUniformIntArrayValues.end(); k++) {
        delete k->second;
    }
    bufferedUniformIntArrayValues.clear();
    for (auto k = bufferedUniformFloatArrayValues.begin(); k != bufferedUniformFloatArrayValues.end(); k++) {
        delete k->second;
    }
    bufferedUniformFloatArrayValues.clear();
    for (auto k = bufferedUniformDoubleArrayValues.begin(); k != bufferedUniformDoubleArrayValues.end(); k++) {
        delete k->second;
    }
    bufferedUniformDoubleArrayValues.clear();

    //bufferedUniformTextureValues.clear();
    //bufferedUniformMatrix3fValues.clear();
    //bufferedUniformMatrix4fValues.clear();

    for (auto k = bufferedUniformVec3fValues.begin(); k != bufferedUniformVec3fValues.end(); k++) {
        delete k->second;
    }
    bufferedUniformVec3fValues.clear();

    for (auto k = bufferedUniformVec4fValues.begin(); k != bufferedUniformVec4fValues.end(); k++) {
        delete k->second;
    }
    bufferedUniformVec4fValues.clear();

    for (auto k = bufferedUniformVec3dValues.begin(); k != bufferedUniformVec3dValues.end(); k++) {
        delete k->second;
    }
    bufferedUniformVec3dValues.clear();

    for (auto k = bufferedUniformVec4dValues.begin(); k != bufferedUniformVec4dValues.end(); k++) {
        delete k->second;
    }
    bufferedUniformVec4dValues.clear();
    arrayVarSizes.clear();
}



void ShaderInterface::deleteUniformVars() {
    clearUniformVarBuffers();
    for (auto iter = uniformVars.begin(); iter != uniformVars.end(); iter++) {
        UniformVariable* v = iter->second;
        delete v;
    }
    uniformVars.clear();
    idToVarMap.clear();
    arrayVarSizes.clear();
}



string ShaderInterface::readSource(string file) {
    ifstream f(file);
    string str((istreambuf_iterator<char>(f)), istreambuf_iterator<char>());
    return str;
}

ShaderInterface.h:

#pragma once

#include "GLAD/include/glad/glad.h"
#include "dependencies/GLFW/include/GLFW/glfw3.h"
#include <string>
#include <unordered_set>
#include <unordered_map>

using namespace std;

class ShaderInterface {


public:

    static int lastShaderUsed;

    enum class UniformVariableType {
        intT, floatT, doubleT, doubleV3T, doubleV4T, floatV3T, floatV4T,
        floatM3T, floatM4T, doubleM3T, doubleM4T, textureT, intAT, floatAT, doubleAT
    };

    bool useTexture;
    bool useBump;
    bool useBlinn;
    bool useColor;

    ShaderInterface(string name, bool useTexture, bool useBump, bool useBlinn, bool useColor);
    ~ShaderInterface();

    void setPathToShaderDir(string path);
    void registerVar(string name, UniformVariableType type);
    void registerVar(string name, UniformVariableType type, int arraySize);
    int getGLId();
    void useMe();
    void setFragmentShader(string code);
    void setVertexShader(string code);
    int getIdForVariable(string varName);
    void sendUniformValues();


private:
    const char* STANDARD_COLOR_TEXTURE_VARNAME = "text";
    const char* STANDARD_NORMALMAP_VARNAME = "normalMap";
    const char* STANDARD_SHADOWMAP_VARNAME = "shadowMap";
    const char* STANDARD_LIGHT_INTENSITY_VARNAME = "lightIntensity";
    const char* STANDARD_LIGHT_COLOR_VARNAME = "lightColor";
    const char* STANDARD_ENVIRON_LIGHT_INTENSITY_VARNAME = "environLightIntensity";
    const char* STANDARD_WORLD_TRANSFORMATION_VARNAME = "transformWorld";
    const char* STANDARD_OBJECT_TRANSFORMATION_VARNAME = "transformObject";
    const char* STANDARD_CAMERA_POS_VARNAME = "cameraPos";
    const char* STANDARD_CAMERA_PROJ_VARNAME = "cameraProjection";
    const char* STANDARD_LIGHT_POS_VARNAME = "lightPosition";
    const char* STANDARD_LIGHT_SPACE_MAT_VARNAME = "lightSpaceMatrix";

    class UniformVariable {
    public:
        string name;
        GLint id;
        UniformVariableType type;
        UniformVariable(string name, GLint id, UniformVariableType type);
    };

    GLint vertexShader, fragmentShader, programID;
    string pathToShaderPrograms;
    string name;
    bool programCompilationFinished;
    unordered_set<string> unknownShaderVarMessages;

    unordered_map<string, UniformVariable*> uniformVars;
    unordered_map<int, UniformVariable*> idToVarMap;
    unordered_map<string, int> arrayVarSizes;

    unordered_map<int, int> bufferedUniformIntValues;
    unordered_map<int, float> bufferedUniformFloatValues;
    unordered_map<int, double> bufferedUniformDoubleValues;
    unordered_map<int, float*> bufferedUniformFloatArrayValues;
    unordered_map<int, double*> bufferedUniformDoubleArrayValues;
    unordered_map<int, int*> bufferedUniformIntArrayValues;
    //unordered_map<int, TextureInterface> bufferedUniformTextureValues;
    //unordered_map<int, Matrix3f> bufferedUniformMatrix3fValues;
    //unordered_map<int, Matrix4f> bufferedUniformMatrix4fValues;
    unordered_map<int, float*> bufferedUniformVec3fValues;
    unordered_map<int, float*> bufferedUniformVec4fValues;
    unordered_map<int, double*> bufferedUniformVec3dValues;
    unordered_map<int, double*> bufferedUniformVec4dValues;

    //static unordered_map<int, TextureInterface> lastUsedTextureUnits;

    static int lastUsedShaderID;


    void initUniformVarBuffers();
    void clearUniformVarBuffers();

    string readSource(string file);
    void finalizeProgramInitialization();

    void logUnknownShaderVar(string msg);

    void deleteUniformVars();
    int checkVarExistence(string name);
    void setUniform(string name, int value);
    void setUniform(string name, int len, int* value);
    void setUniform(string name, float value);
    void setUniform(string name, int len, float* value);
    void setUniform(string name, float v1, float v2, float v3);
    void setUniform(string name, float v1, float v2, float v3, float v4);
    void setUniform(string name, double value);
    void setUniform(string name, int len, double* value);
    void setUniform(string name, double v1, double v2, double v3);
    void setUniform(string name, double v1, double v2, double v3, double v4);
};

I tried commenting out the glValidateProgram() function call made in the ShaderInterface.cpp file at line 103 in the finalizeProgramInitialization() function.

That resulted in a similar runtime error:

libc++abi: terminating with uncaught exception of type std::runtime_error: WARNING: Output of vertex shader 'position' not read by fragment shader

Process finished with exit code 134 (interrupted by signal 6: SIGABRT)

Solution

  • After fixing up your includes to have forward slashes instead of backslashes, I was able to compile your program. Running your program under lldb allows catching the exception and inspecting the state of the program at that time.

    Here is a part of the stack trace:

    frame #9: 0x00000001000047b4 viewportgl`throwInfoLog(int, void (*)(unsigned int, int, int*, char*)) + 168
    frame #10: 0x00000001000048a8 viewportgl`ShaderInterface::finalizeProgramInitialization() + 208
    frame #11: 0x0000000100004e60 viewportgl`ShaderInterface::setFragmentShader(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >) + 308
    frame #12: 0x0000000100026ad0 viewportgl`ViewPortGL::initPlain2DShaders() + 248
    frame #13: 0x00000001000266f4 viewportgl`ViewPortGL::ViewPortGL(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, int, int) + 372
    frame #14: 0x0000000100026bcc viewportgl`ViewPortGL::ViewPortGL(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, int, int) + 44
    frame #15: 0x00000001000261d8 viewportgl`test() + 52
    

    The root cause is that your ViewPortGL::finalizeProgramInitialization method uses glGetUniformLocation to retrieve uniform layouts, and that requires a bound VAO to work. Adding a glBindVertexArray(triangleDataVAO); to the top of that function gets me a screen with a triangle.

    Next time, ask your debugger first!