2. GLSL Validation
GLSL Shader Validation
When developing with GLSL shaders in OpenGL, it is crucial to check your shaders so that you know whether they actually compiled and linked. OpenGL provides status values and information logs for individual shaders and shader programs, which saves a lot of staring at a black window wondering what on earth you broke.
In this tutorial, we will add checks for both vertex and fragment shaders, as well as for the overall shader program. If something goes wrong, OpenGL can give us useful debugging information which we can output to the console.
Why Validate Shaders?
Shaders are small programs, and even a tiny syntax error can stop one from compiling. There are three slightly different things worth checking:
- Compile status: Did each individual shader compile successfully?
- Link status: Did the compiled stages link together into a usable program?
- Program validation: If we explicitly call
glValidateProgram, is the program executable with the current OpenGL state?
The important correction here is that an information log existing does not automatically mean compilation failed. Drivers are allowed to put warnings and useful messages in the log even when the compile/link succeeded. So we check the status first, then print the log when there is one.
Adding Validation Functions
We will add two helper methods to our existing Shader class:
validateShader: Checks the compile status of an individual vertex or fragment shader and prints its information log.validateProgram: Checks the program link status, prints the program information log, and can also run OpenGL’s explicit program validation step.
Shader Validation
The validateShader method takes the shader object and the source filename, which is handy because an error message saying “shader 4 failed” is not quite as friendly as telling us which file we were compiling.
static bool validateShader(GLuint shader, const char *file) {
GLint compiled = GL_FALSE;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
GLint logLength = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 1) {
std::vector<char> log(logLength);
glGetShaderInfoLog(shader, logLength, NULL, &log[0]);
std::cerr << "Shader log for "
<< (file ? file : "(unknown file)")
<< ":\n"
<< &log[0]
<< std::endl;
}
if (compiled == GL_FALSE) {
std::cerr << "Shader compilation failed: "
<< (file ? file : "(unknown file)")
<< std::endl;
return false;
}
return true;
}
Program Validation
After we attach the compiled shader stages and call glLinkProgram, we check GL_LINK_STATUS. This is the bit that tells us whether the program linked successfully.
static bool validateProgram(GLuint program) {
GLint linked = GL_FALSE;
glGetProgramiv(program, GL_LINK_STATUS, &linked);
GLint logLength = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 1) {
std::vector<char> log(logLength);
glGetProgramInfoLog(program, logLength, NULL, &log[0]);
std::cerr << "Program link log:\n"
<< &log[0]
<< std::endl;
}
if (linked == GL_FALSE) {
std::cerr << "Shader program failed to link." << std::endl;
return false;
}
return true;
}
If you also want OpenGL’s explicit validation check while debugging, do it after a successful link and with the state you intend to draw with:
glValidateProgram(shader_id);
GLint valid = GL_FALSE;
glGetProgramiv(shader_id, GL_VALIDATE_STATUS, &valid);
if (valid == GL_FALSE) {
std::cerr << "Shader program failed validation." << std::endl;
}
Do not use GL_VALIDATE_STATUS as a replacement for GL_LINK_STATUS. They answer different questions.
Integrating Validation
To integrate this into the shader class from the previous tutorial, check each shader straight after compilation:
glCompileShader(shader_vp);
bool vertexOk = validateShader(shader_vp, vsFile);
glCompileShader(shader_fp);
bool fragmentOk = validateShader(shader_fp, fsFile);
if (!vertexOk || !fragmentOk) {
return;
}
Then link the program and check the link status:
glLinkProgram(shader_id);
if (!validateProgram(shader_id)) {
return;
}
Full Shader Class Implementation
Here is the important part put together. I am keeping the reusable class approach from the first tutorial because once this works, every later tutorial gets to use the same error checking instead of copy/pasting it everywhere.
#include "shader.h"
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
static std::string textFileRead(const char *fileName) {
std::ifstream file(fileName, std::ios::in);
if (!file) return std::string();
std::ostringstream contents;
contents << file.rdbuf();
return contents.str();
}
static bool validateShader(GLuint shader, const char *file) {
GLint compiled = GL_FALSE;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
GLint logLength = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 1) {
std::vector<char> log(logLength);
glGetShaderInfoLog(shader, logLength, NULL, &log[0]);
std::cerr << "Shader log for "
<< (file ? file : "(unknown file)")
<< ":\n" << &log[0] << std::endl;
}
return compiled == GL_TRUE;
}
static bool validateProgram(GLuint program) {
GLint linked = GL_FALSE;
glGetProgramiv(program, GL_LINK_STATUS, &linked);
GLint logLength = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 1) {
std::vector<char> log(logLength);
glGetProgramInfoLog(program, logLength, NULL, &log[0]);
std::cerr << "Program link log:\n"
<< &log[0] << std::endl;
}
return linked == GL_TRUE;
}
Shader::Shader()
: shader_id(0), shader_vp(0), shader_fp(0) {
}
Shader::Shader(const char *vsFile, const char *fsFile)
: shader_id(0), shader_vp(0), shader_fp(0) {
init(vsFile, fsFile);
}
void Shader::init(const char *vsFile, const char *fsFile) {
std::string vsText = textFileRead(vsFile);
std::string fsText = textFileRead(fsFile);
if (vsText.empty() || fsText.empty()) {
std::cerr << "Either vertex shader or fragment shader file not found." << std::endl;
return;
}
const GLchar *vsSource = vsText.c_str();
const GLchar *fsSource = fsText.c_str();
shader_vp = glCreateShader(GL_VERTEX_SHADER);
shader_fp = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(shader_vp, 1, &vsSource, NULL);
glShaderSource(shader_fp, 1, &fsSource, NULL);
glCompileShader(shader_vp);
glCompileShader(shader_fp);
if (!validateShader(shader_vp, vsFile) ||
!validateShader(shader_fp, fsFile)) {
return;
}
shader_id = glCreateProgram();
glAttachShader(shader_id, shader_vp);
glAttachShader(shader_id, shader_fp);
glLinkProgram(shader_id);
validateProgram(shader_id);
}
Source & downloads
Questions or corrections? If something doesn’t work or you spot something I’ve got wrong, leave a comment below. These tutorials have been around a long time, so useful corrections are always welcome!
Swiftless,
In the function
static void validateProgram(GLuint program);
You need to call validate before fetching the log, ( at least with my drivers ). With my drivers the log is lazily populated.
Simply reordering the function gives the correct behavior.
static void validate_program(GLuint program) {
char buffer[BUFFER_SIZE];
memset(buffer, 0, BUFFER_SIZE);
GLsizei length = 0;
GLint status = GL_FALSE;
glValidateProgram(program); // Get OpenGL to try validating the program
glGetProgramiv(program, GL_VALIDATE_STATUS, &status); // Find out if the shader program validated correctly
if (status == GL_FALSE) { // If there was a problem validating
std::cerr << "Error validating program " << program < 0) // If we have any information to display
std::cerr << "Program " << program << " link error: " << buffer << std::endl; // Output the information
}
}
outputs:
Error validating program3
Program 3 link error: Validation Failed: Current draw framebuffer is invalid.
( Why I get that is a bit more complicated. I think it is because I have a headless GL context )
Hi Swiftless,
(Firts of all sorry if my english isn’t much well)
When I run the program using your code it give me this error:
Exception first-chance at 0x00000000 in myProgram.exe: 0xC0000005: Access violation.
Unhandled exception at 0x00000000 in myProgram.exe: 0xC0000005: Access violation.
I don’t know how ot fix that, If you have any advice I would be grateful.
Thak
PS: I use Visual C++ 2010
hey swiftless,
You don’t need to assume a buffer size of 512.
Instead of
//code
const unsigned int BUFFER_SIZE = 512;
why not
glGetShaderiv(shader, GL_INFO_LOG_LENGTH , &BUFFER_SIZE);
Or something similar.
see http://www.khronos.org/opengles/sdk/2.0/docs/man/glGetShaderiv.xml
Hi Dr Deo,
That is absolutely correct! I’m not sure why I didn’t do that in this tutorial.
Thanks,
Swiftless