2. GLSL Validation
GLSL Shader Validation
When developing with GLSL shaders in OpenGL, it is crucial to validate your shaders to ensure they are error-free and properly compiled. OpenGL provides mechanisms to check for errors in individual shaders and shader programs, helping you diagnose issues during development.
In this tutorial, we will show you how to implement validation for both vertex and fragment shaders, as well as for the overall shader program. If an error occurs, OpenGL will provide useful debugging information, which we can output to the console.
Why Validate Shaders?
Shaders are small programs that run directly on the GPU, and even minor syntax errors or logical issues can prevent them from compiling or linking. Validation ensures:
– Individual shaders are correctly compiled.
– Shader programs are properly linked.
– The shaders are compatible with the current OpenGL state.
By incorporating validation into your development process, you can save time debugging and ensure your shaders work as intended.
Adding Validation Functions
We will add two new methods to our existing `Shader` class:
1. `validateShader`: Checks for errors in individual vertex and fragment shaders.
2. `validateProgram`: Validates the entire shader program after linking.
Shader Validation
The `validateShader` method takes two parameters:
– The shader object to validate.
– The file name of the shader source code for debugging context.
Here is the implementation:
- static void validateShader(GLuint shader, const char *file = nullptr) {
- const unsigned int BUFFER_SIZE = 512; // Size of the error buffer
- char buffer[BUFFER_SIZE]; // Buffer to store error messages
- memset(buffer, 0, BUFFER_SIZE); // Clear the buffer
- GLsizei length = 0; // Length of the error message
- // Get the shader compilation log
- glGetShaderInfoLog(shader, BUFFER_SIZE, &length, buffer);
- // Output any errors to the console
- if (length > 0) {
- std::cerr << "Shader " << shader << " (" << (file ? file : "")
- << ") compile error: " << buffer << std::endl;
- }
- }
Program Validation
The `validateProgram` method checks the overall shader program after linking:
- static void validateProgram(GLuint program) {
- const unsigned int BUFFER_SIZE = 512; // Size of the error buffer
- char buffer[BUFFER_SIZE]; // Buffer to store error messages
- memset(buffer, 0, BUFFER_SIZE); // Clear the buffer
- GLsizei length = 0; // Length of the error message
- // Get the program linking log
- glGetProgramInfoLog(program, BUFFER_SIZE, &length, buffer);
- // Output any linking errors
- if (length > 0) {
- std::cerr << "Program " << program << " link error: " << buffer << std::endl;
- }
- // Validate the program
- glValidateProgram(program);
- GLint status;
- glGetProgramiv(program, GL_VALIDATE_STATUS, &status);
- // Check if validation passed
- if (status == GL_FALSE) {
- std::cerr << "Error validating shader program " << program << std::endl;
- }
- }
Integrating Validation
To integrate validation into your existing shader setup:
1. Validate each shader immediately after compiling:
- glCompileShader(shader_vp);
- validateShader(shader_vp, vsFile);
- glCompileShader(shader_fp);
- validateShader(shader_fp, fsFile);
2. Validate the shader program after linking:
- glLinkProgram(shader_id);
- validateProgram(shader_id);
Full Shader Class Implementation
Here’s the full updated implementation of the `Shader` class, including validation methods:
- #include "shader.h"
- #include <cstring>
- #include <iostream>
- #include <cstdlib>
- static char *textFileRead(const char *fileName) {
- char *text = nullptr;
- if (fileName != nullptr) {
- FILE *file = fopen(fileName, "rt");
- if (file != nullptr) {
- fseek(file, 0, SEEK_END);
- int count = ftell(file);
- rewind(file);
- if (count > 0) {
- text = (char *)malloc(sizeof(char) * (count + 1));
- count = fread(text, sizeof(char), count, file);
- text[count] = '\0';
- }
- fclose(file);
- }
- }
- return text;
- }
- Shader::Shader() {}
- Shader::Shader(const char *vsFile, const char *fsFile) {
- init(vsFile, fsFile);
- }
- void Shader::init(const char *vsFile, const char *fsFile) {
- shader_vp = glCreateShader(GL_VERTEX_SHADER);
- shader_fp = glCreateShader(GL_FRAGMENT_SHADER);
- const char *vsText = textFileRead(vsFile);
- const char *fsText = textFileRead(fsFile);
- if (vsText == nullptr || fsText == nullptr) {
- std::cerr << "Either vertex shader or fragment shader file not found." << std::endl;
- return;
- }
- glShaderSource(shader_vp, 1, &vsText, nullptr);
- glShaderSource(shader_fp, 1, &fsText, nullptr);
- glCompileShader(shader_vp);
- validateShader(shader_vp, vsFile);
- glCompileShader(shader_fp);
- validateShader(shader_fp, fsFile);
- shader_id = glCreateProgram();
- glAttachShader(shader_id, shader_fp);
- glAttachShader(shader_id, shader_vp);
- glLinkProgram(shader_id);
- validateProgram(shader_id);
- }
- Shader::~Shader() {
- glDetachShader(shader_id, shader_fp);
- glDetachShader(shader_id, shader_vp);
- glDeleteShader(shader_fp);
- glDeleteShader(shader_vp);
- glDeleteProgram(shader_id);
- }
- void Shader::bind() {
- glUseProgram(shader_id);
- }
- void Shader::unbind() {
- glUseProgram(0);
- }
- </cstdlib></iostream></cstring>
Download Links
If you have any questions, feel free to email me at swiftless@gmail.com.
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