Problems with uniform blocks

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;
}

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.

It’s “yellow” here (NVidia 190.32 Linux drivers, GTX285).

Only change I made was to get rid of GLEW and use glext.h. The GLEW on my platform isn’t new enough.

All three variants were yellow (as written, without the “unused = dummy”, and with all the uniforms in one block)?

Can anybody else reproduce this? I’ve tried it on 4-5 different computers, and it’s been incorrect/unreliable every time… I hope I’m not just nuts!

  • As written => yellow
  • Without the “unused = dummy” => yellow
  • With all the uniforms in one block => yellow

Again, NVidia 190.32 Linux drivers.

Note that for the latter case I of course had to add the else in this bit of logic:


  if (numUniformBlocks == 2)
  {
    greenBlockIndex  = glGetUniformBlockIndex(prog_id, "GreenBlock");
  }
  else
    greenBlockIndex = redBlockIndex;

as well as not upload the 2nd block (as there isn’t one).

I made all the changes you mentioned; I replaced GLEW with glext.h, added the “else greenBlockIndex = redBlockIndex;” lines, and wrapped the 2nd block upload as follows:

if (numUniformBlocks > 1)
{
	glBindBuffer(GL_UNIFORM_BUFFER, ubo[greenBlockIndex]);
	glBufferData(GL_UNIFORM_BUFFER, uboSize[greenBlockIndex], uniforms1, GL_DYNAMIC_DRAW);
	glBindBuffer(GL_UNIFORM_BUFFER, 0);
}

The program now reliably draws yellow for both the “no dummy” and the “all uniforms in one block” cases. However, it still draws green for the default “as written” case. So, there’s still a bug somewhere…

we were able to reproduce that issue, and a fix should be available in catalyst in 2-3 months.

Sweet! Thanks Pierre!

This problem seems to be fixed in the Catalyst 10.5 drivers released today. Thanks again!

Sweet!