OpenGL Shader binaries not linking in C++ - c++

I am currently working on a OpenGL Shader class, that uses SPIR-V to compile the shaders. Getting the compiled binaries from SPIR-V works fine, the problem is, that my program crashes with the OpenGL exception GL_INVALID_OPERATION error generated. <program> has not been linked, or is not a program object. when trying to bind the Shader with glUseProgram(m_RendererID).
This is my shader creation function:
void OpenGLShader::LoadAndCreateShaders(const std::unordered_map<GLenum, std::vector<uint32>> &shaderData)
{
if (m_RendererID)
glDeleteProgram(m_RendererID);
GLuint program = glCreateProgram();
m_RendererID = program;
std::vector<GLuint> shaderRendererIds;
shaderRendererIds.reserve(shaderData.size());
for (auto &[stage, data] : shaderData)
{
GLuint shaderId = glCreateShader(stage);
glShaderBinary(1, &shaderId, GL_SHADER_BINARY_FORMAT_SPIR_V, data.data(), (uint32)data.size());
glSpecializeShader(shaderId, "main", 0, nullptr, nullptr);
glAttachShader(program, shaderId);
GLint status;
glGetShaderiv(shaderId, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
std::cout << "Shader compilation failed" << std::endl;
}
shaderRendererIds.emplace_back(shaderId);
}
// Link shader program
glLinkProgram(program);
int32 isLinked = 0;
glGetProgramiv(program, GL_LINK_STATUS, (int32*)&isLinked);
if (isLinked == GL_FALSE)
{
int32 maxLength = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
if (maxLength > 0)
{
std::vector<GLchar> infoLog(maxLength);
glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]);
std::cout << "Shader linking failed: " << &infoLog[0] << std::endl;
glDeleteProgram(program);
for (auto id : shaderRendererIds)
glDeleteShader(id);
}
}
for (auto id : shaderRendererIds)
glDetachShader(program, id);
}
This function basically takes in the compiled sources from SPIR-V and tries to create the OpenGL shader. When I debugged the program, I found out that after glLinkProgram() isLinked stays 0, but the maxLength variable stays also 0. So the linking process failed, but didn't give an error. Did anyone have the same issue before?

Related

Why does glGetProgramInfoLog return an empty string?

I'm working off a code base that I know compiles correctly. Currently, I've introduced a purposeful spelling mistake into my shader to simulate an error. At this point, the code refuses to compile. I can then call
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &params);
to find that params is now 108 where is was previously 0, so I know that there is a log associated with this shader (which I have tried to compile). However, when I call
glGetProgramInfoLog(shader, 512, &size, ErrorLog);
size returns 0, and ErrorLog is still empty.
Here's the important code in this situation:
void Shader::AddShader(GLuint ShaderProgram, const char* pShaderText, GLenum ShaderType)
{
GLuint ShaderObj = glCreateShader(ShaderType);
if (ShaderObj == 0) {
fprintf(stderr, "Error creating shader type %d\n", ShaderType);
exit(1);
}
const char* pShaderSource = readShaderSource(pShaderText);
glShaderSource(ShaderObj, 1, (const GLchar**)&pShaderSource, NULL);
glCompileShader(ShaderObj);
checkCompileError(ShaderObj, ShaderType);
glAttachShader(ShaderProgram, ShaderObj);
}
bool Shader::checkCompileError(GLuint shader, GLenum ShaderType)
{
GLint params = -1;
GLchar ErrorLog[512] = { 0 };
GLint size = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &params);
if (GL_TRUE != params) {
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &params);
glGetProgramInfoLog(shader, 512, &size, ErrorLog);
fprintf(stderr, "Error compiling shader type %d: '%s'\n", ShaderType, ErrorLog);
exit(1);
}
return true;
}
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &params);
glGetProgramInfoLog(shader, 512, &size, ErrorLog);
^^^^^^^ should be Shader
You're compiling & checking a shader so you should use glGetShaderInfoLog().
glGetProgramInfoLog() is for grabbing the link log of the complete shader program object you get from glCreateProgram()

glGetProgramIv claims that shader linking failed, but glGetProgramInfoLog is empty

I am currently attempting to write a basic graphics engine with OpenGl and C++. In doing so, OpenGl is behaving in a very strange way with no apparent explanation. It claims that a shader failed to compile, but then refuses to give any sort of error explaining why. Specifically, when I call glGetProgramiv the returned value is 0, but glGetProgramInfoLog gives an empty string¹.
Here is a minimum complete verifiable example:
#include <SDL2/SDL.h>
#include <GL/glew.h>
#include <sstream>
#include <stdexcept>
#include <fstream>
GLuint loadShader(const std::string &path, GLenum type);
int main(int argc, char *argv[]){
//show window & other housekeeping
SDL_Init(SDL_INIT_EVERYTHING);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_Window *window = SDL_CreateWindow("test window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
SDL_GLContext glContext = SDL_GL_CreateContext(window);
SDL_GL_MakeCurrent(window, glContext);
GLenum c = glewInit();
if(c != GLEW_OK){
std::ostringstream sout;
sout << "Failed to initialize OpenGl: " << glewGetErrorString(c);
throw std::runtime_error(sout.str());
}
//**** -- BEGIN IMPORTANT BIT -- ****//
//This is where the shaders are loaded
//load shaders
GLuint vertexShader = loadShader("shader.vert", GL_VERTEX_SHADER);
GLuint fragmentShader = loadShader("shader.frag", GL_FRAGMENT_SHADER);
//create & link program
GLuint program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
//check for errors
GLint success;
glGetProgramiv(program, GL_LINK_STATUS, &success);
if(!success){
GLchar infoLog[512];
GLint size; //gives 0 when checked in debugger
glGetProgramInfoLog(program, 512, &size, infoLog);
throw std::runtime_error(std::string("Failed to link shader program: ") + infoLog);
}
//cleanup
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
//**** -- END IMPORTANT BIT -- ****//
//update loop
bool isClosed = false;
while(!isClosed){
SDL_GL_SwapWindow(window);
SDL_Event e;
while(SDL_PollEvent(&e)){
switch(e.type){
case SDL_QUIT:
isClosed = true;
break;
}
}
}
}
GLuint loadShader(const std::string &path, GLenum type){
//load file
std::ifstream fin(path);
std::string line, total = "";
while(std::getline(fin, line)){
total += (line + "\n");
}
//reformat source into OpenGl's ridiculous format
const char *cArray = total.c_str();
const char **pointerToCArray = &cArray;
//create shader
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, pointerToCArray, nullptr);
glCompileShader(shader);
//check for erros
GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if(!success){
GLchar infoLog[512];
glGetShaderInfoLog(shader, 512, nullptr, infoLog);
throw std::runtime_error(std::string("Failed to compile shader: ") + infoLog);
}
}
MCVE testshader.vert:
#version 330 core
layout (location = 0) in vec3 position;
void main()
{
gl_Position = vec4(position.x, position.y, position.z, 1.0);
}
MCVE testshader.frag:
#version 330 core
out vec4 color;
void main()
{
color = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}
When I run this I get the following output in addition to a brief flash of a window:
terminate called after throwing an instance of 'std::runtime_error'
what(): Failed to link shader program:
Aborted (core dumped)
Extra Notes:
I am using Ubuntu 16.10 x64
I am using a Nvidia GTX 1070 with driver version 375.39
The SDL and glew versions are those installed from the libsdl2-dev and libglew-dev packages respectively. I am using pkg-config to link.
During my research to solve this error I mostly found various forum and StackOverflow posts that found specific errors in other people's code. None of these apply to my code, and after finding this error I compared my code to this supposedly working example. This example also compiles and runs correctly on my computer, which rules out any sort of configuration error.
¹: It is not possible that glGetProgramInfoLog is doing nothing with external consequences and that the infoLog array happened to be allocated with a null-terminator character at its first address because the size return of glGetProgramInfoLog returns 0 even if set to a different value before glGetProgramInfoLog is called.
I have found the issue: a missing return statement in the loadShader() method. This, however, as I experienced when attempting to solve this was not helpful. As such, I recommend to any future person reading this that you find a working example (ie. this one) that does something similar as what you are trying to do and copy/paste progressively smaller parts of its code into yours (or visa-versa) to do a kind of binary search to find your error.

OpenGL not reporting shader compiler errors

I'm having trouble getting OpenGL to work properly. It fails on all shaders and simply returns an info log with the length of 0.
To see if it was in fact OpenGL, I created a little piece of code to test it:
GLuint s = glCreateShader(GL_VERTEX_SHADER);
const char *src = "bad\nsource\ncode";
glShaderSource(s, 1, &src, 0);
GLint len;
glGetShaderiv(s, GL_INFO_LOG_LENGTH, &len);
std::vector<char> buffer(len+1);
glGetShaderInfoLog(s, len, 0, buffer.data());
buffer[len] = '\0';
std::cout << len << std::endl;
std::cout << buffer.data() << std::endl;
And when ran, it simply prints 0 to the console.
What's happening?
You need to compile your shader in order to get an errorlog.
Pseudo Code:
glCompileShader(shader);
GLint status;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if(status == GL_FALSE) {
GLint infolog_length;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infolog_length);
GLchar[] infolog = new GLchar[infolog_length+1];
glGetShaderInfoLog(shader, infolog_length, null, infolog.ptr);
// print infolog etc.
}
From The Documentation:
glGetShaderInfoLog returns the information log for the specified shader object. The information log for a shader object is modified when the shader is compiled. The string that is returned will be null terminated.

syntax error: unexpected $end at token "<EOF>" GLSL with any shader

I've seen other similar questions, and I now that it's usually due to wrong shader loading code, but I've tried with lots of different ways to load them, and I always get this error. Here is the loading code I'm currently using (for the Vertex shader, i do the same for the fragment shader):
GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
std::string VertexShaderCode;
std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
if(VertexShaderStream.is_open()){
std::string Line = "";
while(getline(VertexShaderStream, Line))
VertexShaderCode += "\n" + Line;
VertexShaderStream.close();
}
GLint Result = GL_FALSE;
int InfoLogLength;
// Compile Vertex Shader
printf("Compiling shader : %s\n", vertex_file_path);
char const * VertexSourcePointer = VertexShaderCode.c_str();
glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);
glCompileShader(VertexShaderID);
// Check Vertex Shader
glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
std::vector<char> VertexShaderErrorMessage(InfoLogLength);
glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
fprintf(stdout, "%s\n", &VertexShaderErrorMessage[0]);
I've also tried this:
GLuint VertexShaderID = glCreateShader(GL_FRAGMENT_SHADER);
std::ifstream shaderFile(vertex_file_path);
//Error out here.
stringstream shaderData;
shaderData << shaderFile.rdbuf(); //Loads the entire string into a string stream.
shaderFile.close();
const std::string &VertexShaderCode = shaderData.str(); //Get the string stream as a std::string.
GLint Result = GL_FALSE;
int InfoLogLength;
printf("Compiling shader : %s\n", vertex_file_path);
char const * VertexSourcePointer = VertexShaderCode.c_str();
glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);
glCompileShader(VertexShaderID);
// Check Vertex Shader
glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
std::vector<char> VertexShaderErrorMessage(InfoLogLength);
glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
fprintf(stdout, "%s\n", &VertexShaderErrorMessage[0]);
And this is the linking code:
// Link the program
fprintf(stdout, "Linking program\n");
GLuint ProgramID = glCreateProgram();
glAttachShader(ProgramID, VertexShaderID);
glAttachShader(ProgramID, FragmentShaderID);
glLinkProgram(ProgramID);
// Check the program
glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
std::vector<char> ProgramErrorMessage( max(InfoLogLength, int(1)) );
glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
fprintf(stdout, "%s\n", &ProgramErrorMessage[0]);
I have also tried passing (GLint *)VertexShaderCode.size() instead of NULL to the length parameter of glShaderSource(), but I get the same errors. I get those errors both when compiling and when linking
In case it helps, I'm using Opengl 3.3, GLEW, GLFW, Visual c++ 2010, and windows 7 64 bits
How did you pass the length parameter to glShaderSource. The typecast you performed should trigger you a thick fat warning. The proper way would have been to put the result of size() into a variable. And then you give the pointer to that variable to glShaderSource.

Setting up basic shader program - GLchar and file_contents undefined?

I am trying to experiment using basic shaders in my program, I came across a nice tutorial that talks you through writing a basic shader "util class" i guess you would call it? Which should allow me to apply a vertex and fragment shader...So I linked glew to my project (i have also glu, glut and glaux included) and inserted the following into a header file
#include "include\gl\glew.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
static struct {
/* ... fields for buffer and texture objects */
GLuint vertex_shader, fragment_shader, program;
struct {
GLint fade_factor;
GLint textures[2];
} uniforms;
struct {
GLint position;
} attributes;
GLfloat fade_factor;
} g_resources;
static GLuint make_shader(GLenum type, const char *filename)
{
GLint length;
char *source = file_content(filename, &length);
GLuint shader;
GLint shader_ok;
if (!source)
return 0;
shader = glCreateShader(type);
glShaderSource(shader, 1, (const GLchar**)&source, &length);
free(source);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_ok);
if (!shader_ok) {
fprintf(stderr, "Failed to compile %s:\n", filename);
show_info_log(shader, glGetShaderiv, glGetShaderInfoLog);
glDeleteShader(shader);
return 0;
}
return shader;
}
static void show_info_log(
GLuint object,
PFNGLGETSHADERIVPROC glGet__iv,
PFNGLGETSHADERINFOLOGPROC glGet__InfoLog)
{
GLint log_length;
char *log;
glGet__iv(object, GL_INFO_LOG_LENGTH, &log_length);
log = malloc(log_length);
glGet__InfoLog(object, log_length, NULL, log);
fprintf(stderr, "%s", log);
free(log);
}
static GLuint make_program(GLuint vertex_shader, GLuint fragment_shader)
{
GLint program_ok;
GLuint program = glCreateProgram();
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &program_ok);
if (!program_ok) {
fprintf(stderr, "Failed to link shader program:\n");
show_info_log(program, glGetProgramiv, glGetProgramInfoLog);
glDeleteProgram(program);
return 0;
}
return program;
}
static int make_resources(void)
{
/* make buffers and textures ... */
g_resources.vertex_shader = make_shader(
GL_VERTEX_SHADER,
"hello-gl.v.glsl"
);
if (g_resources.vertex_shader == 0)
return 0;
g_resources.fragment_shader = make_shader(
GL_FRAGMENT_SHADER,
"hello-gl.f.glsl"
);
if (g_resources.fragment_shader == 0)
return 0;
g_resources.program = make_program(
g_resources.vertex_shader,
g_resources.fragment_shader
);
if (g_resources.program == 0)
return 0;
g_resources.uniforms.fade_factor
= glGetUniformLocation(g_resources.program, "fade_factor");
g_resources.uniforms.textures[0]
= glGetUniformLocation(g_resources.program, "textures[0]");
g_resources.uniforms.textures[1]
= glGetUniformLocation(g_resources.program, "textures[1]");
g_resources.attributes.position
= glGetAttribLocation(g_resources.program, "position");
return 1;
}
But my compiler complains about the following:
9 IntelliSense: "GLchar" is not a type name
11 IntelliSense: a value of type "void *" cannot be assigned to an entity of type "char *"
7 IntelliSense: identifier "file_contents" is undefined
5 IntelliSense: identifier "GLchar" is undefined
Am I missing something? I searched the internet and it seems like GLchar and the file_contents function do exist?
The error line you gave is the compiler telling you, that it doesn't know about the type GLchar, which is defined in GL/gl.h – or GL/glew.h in your case, which also defines it. But it seems to be not properly included.
Your first line should be
#include <GL/glew.h>
i.e. uppercae GL, a forward slash (backslashes are a Microsoaft addition, but forwards are accepted just fine), and the whole thing in angle brackets, as you want to include from the standard includes.
Next you should not link against glaux. That one is so outdated that it's become toxic.
If you want a working OpenGL shader example program, I prepared one at https://github.com/datenwolf/codesamples/tree/master/samples/OpenGL/minimal_glsl