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)
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!