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:

  1. validateShader: Checks the compile status of an individual vertex or fragment shader and prints its information log.
  2. 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!

  • March 25, 2010
  • 4