I’m having some unpredictable problems with uniform blocks, where the uniform values I’m getting in my shader aren’t the ones I’m expecting. At first I thought it was specifically a problem with multiple uniform blocks, but now I’m not so sure.
Basically, here’s my GLSL program (vertex and fragment shaders):
#version 150 core
precision highp float;
layout(std140) uniform RedBlock
{
float dummy;
float red;
};
layout(std140) uniform GreenBlock
{
float green;
};
in vec4 in_Position;
void main(void)
{
float unused = dummy;
gl_Position = in_Position;
}
#version 150 core
precision highp float;
layout(std140) uniform RedBlock
{
float dummy;
float red;
};
layout(std140) uniform GreenBlock
{
float green;
};
// Vertex shader
in vec4 in_Position;
void main(void)
{
float unused = dummy;
gl_Position = in_Position;
}
out vec4 out_Color;
void main(void)
{
out_Color = vec4(red,green,0,1);
}
In my app, I’m setting the “red” and “green” uniforms to 1.0. When I draw a full-screen quad with this program, I expect to see a yellow quad. Instead, sometimes it’s green. I’ve checked and double-checked all the block binding points, uniform offsets, uniform sizes and so on; everything looks correct. At no point is glError() returning any failure codes.
Strangely, I can get different results by making subtle changes to the shader code. If I comment out the “float unused = dummy;” line in the vertex shader (which should have absolutely no effect on the output), the results seem to be consistently correct (yellow quad). Even wierder: if I eliminate the “GreenBlock” entirely and combine all three uniforms into one block, the vertex positions seem to get corrupted, and the resulting quad is no longer necessarily drawn at all (or is drawn in the upper-right corner of the screen, instead of centered)!
Most of my testing has been on Windows using ATI cards (a 3450, a 4870 and a 5850, Catalyst drivers 9.11-10.2), but I’ve run on a couple of NVIDIA cards with similarly weird results (black quad, even though I’m setting the clear color to magenta).
The longer I test this, the more inconsistencies I notice. Am I missing something in my setup code? I’ve reproduced the bug in a fairly small, relatively self-contained test program, using GLUT for windowing and GLEW for extension loading. Here’s the code:
#include "GL/glew.h"
#pragma comment(lib, "glew32.lib")
#include "GL/openglut.h"
#include <assert.h>
#include <stdio.h>
// Vertex positions for a full-screen quad, pre-transformed into clip space.
float vertices[16] =
{
-1, -1, 0, 1,
1, -1, 0, 1,
-1, 1, 0, 1,
1, 1, 0, 1,
};
GLuint vbo;
GLuint vao;
GLint redBlockIndex = -1;
GLint greenBlockIndex = -1;
GLint redBindPoint = -1;
GLint greenBindPoint = -1;
GLuint ubo[2] = {0,0}; // uniform buffer objects
GLint uboSize[2] = {0,0}; // sizes will be read from the program
float uniforms0[4] =
{
0, 0, 0, 0,
};
float uniforms1[4] =
{
0, 0, 0, 0,
};
GLuint prog_id = 0;
const char *vpSource =
"
\
#version 150 core
\
precision highp float;
\
layout(std140) uniform RedBlock
\
{
\
float dummy;
\
float red;
\
};
\
layout(std140) uniform GreenBlock
\
{
\
float green;
\
};
\
in vec4 in_Position;
\
void main(void)
\
{
\
float unused = dummy;
\
gl_Position = in_Position;
\
}
\
";
const char *fpSource =
"
\
#version 150 core
\
precision highp float;
\
layout(std140) uniform RedBlock
\
{
\
float dummy;
\
float red;
\
};
\
layout(std140) uniform GreenBlock
\
{
\
float green;
\
};
\
out vec4 out_Color;
\
void main(void)
\
{
\
out_Color = vec4(red,green,0,1);
\
}
\
";
static void init(void)
{
// Create vertex array object
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// Create vertex buffer
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, 16*sizeof(float), vertices, GL_STATIC_DRAW);
const GLuint positionAttribIndex = 0;
glVertexAttribPointer(positionAttribIndex, 4, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(positionAttribIndex);
glBindVertexArray(0);
prog_id = glCreateProgram();
// Vertex program
GLuint vpShader = glCreateShader(GL_VERTEX_SHADER);
assert(vpShader != 0);
glShaderSource(vpShader, 1, &vpSource, NULL);
glCompileShader(vpShader);
int vpStatus = 0;
glGetShaderiv(vpShader, GL_COMPILE_STATUS, &vpStatus);
{
char infoLog[8192];
glGetShaderInfoLog(vpShader, 8192, NULL, infoLog);
printf("vertex shader compile log:
%s
", infoLog);
}
assert(vpStatus == 1);
glAttachShader(prog_id, vpShader);
glDeleteShader(vpShader);
// Fragment program
GLuint fpShader = glCreateShader(GL_FRAGMENT_SHADER);
assert(fpShader != 0);
glShaderSource(fpShader, 1, &fpSource, NULL);
glCompileShader(fpShader);
int fpStatus = 0;
glGetShaderiv(fpShader, GL_COMPILE_STATUS, &fpStatus);
{
char infoLog[8192];
glGetShaderInfoLog(fpShader, 8192, NULL, infoLog);
printf("fragment shader compile log:
%s
", infoLog);
}
assert(fpStatus == 1);
glAttachShader(prog_id, fpShader);
glDeleteShader(fpShader);
// Attribute indices
glBindAttribLocation(prog_id, positionAttribIndex, "in_Position");
// Link the program object
glLinkProgram(prog_id);
int status = 0;
glGetProgramiv(prog_id, GL_LINK_STATUS, &status);
{
char infoLog[8192];
glGetProgramInfoLog(prog_id, 8192, NULL, infoLog);
printf("program link log:
%s
", infoLog);
}
assert(status == 1);
// Query program for uniform block data
GLint numUniformBlocks = -1;
glGetProgramiv(prog_id, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBlocks);
redBlockIndex = glGetUniformBlockIndex(prog_id, "RedBlock");
if (numUniformBlocks == 2)
{
greenBlockIndex = glGetUniformBlockIndex(prog_id, "GreenBlock");
}
// Read each block's size.
glGetActiveUniformBlockiv(prog_id, redBlockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &(uboSize[redBlockIndex]));
if (numUniformBlocks == 2)
{
glGetActiveUniformBlockiv(prog_id, greenBlockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &(uboSize[greenBlockIndex]));
}
// Locate green and red variables, and set both values to 1.0 in the local uniforms array(s)
const char *names[2] = {"red", "green"};
GLuint uniformIndices[2] = {0,0};
glGetUniformIndices(prog_id, 2, names, uniformIndices);
if (uniformIndices[0] >= 0 && uniformIndices[1] >= 0)
{
GLuint uniformBlockIndices[2];
GLuint uniformOffsets[2];
glGetActiveUniformsiv(prog_id, 2, uniformIndices, GL_UNIFORM_BLOCK_INDEX, (GLint*)uniformBlockIndices);
glGetActiveUniformsiv(prog_id, 2, uniformIndices, GL_UNIFORM_OFFSET, (GLint*)uniformOffsets);
float *uniformBlocks[2] = {uniforms0, uniforms1};
assert(uniformBlockIndices[0] < (GLuint)numUniformBlocks);
assert(uniformBlockIndices[1] < (GLuint)numUniformBlocks);
assert(uniformOffsets[0]+4 <= (GLuint)uboSize[uniformBlockIndices[0]]);
assert(uniformOffsets[1]+4 <= (GLuint)uboSize[uniformBlockIndices[1]]);
uniformBlocks[uniformBlockIndices[0]][uniformOffsets[0]/4] = 1.0f; // red
uniformBlocks[uniformBlockIndices[1]][uniformOffsets[1]/4] = 1.0f; // green
}
// Allocate uniform buffer objects, and initialize them with the local block contents
glGenBuffers(2, ubo);
glBindBuffer(GL_UNIFORM_BUFFER, ubo[redBlockIndex]);
glBufferData(GL_UNIFORM_BUFFER, uboSize[redBlockIndex], uniforms0, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glBindBuffer(GL_UNIFORM_BUFFER, ubo[greenBlockIndex]);
glBufferData(GL_UNIFORM_BUFFER, uboSize[greenBlockIndex], uniforms1, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
// Set up the binding points for each block. For simplicity,
// we map each block index to the same binding point index.
GLint maxNumUniformBufferBindingPoints = -1;
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxNumUniformBufferBindingPoints);
redBindPoint = redBlockIndex;
greenBindPoint = greenBlockIndex;
assert(redBindPoint < maxNumUniformBufferBindingPoints);
assert(greenBindPoint < maxNumUniformBufferBindingPoints);
glUniformBlockBinding(prog_id, redBlockIndex, redBindPoint);
if (numUniformBlocks > 1)
{
glUniformBlockBinding(prog_id, greenBlockIndex, greenBindPoint);
}
// Attach the buffers to the uniform binding points
glBindBufferBase(GL_UNIFORM_BUFFER, redBindPoint, ubo[redBlockIndex]);
if (numUniformBlocks > 1)
{
glBindBufferBase(GL_UNIFORM_BUFFER, greenBindPoint, ubo[greenBlockIndex]);
}
// cleanup
glUseProgram(0);
{
GLenum errorCode = glGetError();
assert(errorCode == GL_NO_ERROR);
}
}
static void renderScene(void)
{
glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
// set state
glUseProgram(prog_id);
glBindVertexArray(vao);
// Validate before use
static bool firstTime = true;
if (firstTime)
{
firstTime = false;
glValidateProgram(prog_id);
GLint status = 0;
glGetProgramiv(prog_id, GL_VALIDATE_STATUS, &status);
{
char infoLog[8192];
glGetProgramInfoLog(prog_id, 8192, NULL, infoLog);
printf("program validation log:
%s
", infoLog);
}
assert(status == 1);
}
// Draw full-screen quad, pre-projected into clip space.
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// cleanup
glBindVertexArray(0);
glUseProgram(0);
{
GLenum errorCode = glGetError();
assert(errorCode == GL_NO_ERROR);
}
glFlush();
}
int main(int argc, char **argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_SINGLE | GLUT_RGBA);
glutInitWindowPosition(100,100);
glutInitWindowSize(320,320);
glutCreateWindow("Basic GLUT Project");
glutDisplayFunc(renderScene);
GLenum err = glewInit();
if (!GLEW_ARB_uniform_buffer_object)
{
printf("GLEW says: GL_ARB_uniform_buffer_object is not supported!
");
return 0;
}
init();
glutMainLoop();
return 0;
}