AMD Bug: glDrawArrays with 3-Component GL_UNSIGNED_BYTE VBO

Hello,

we observe inconsistent behavior with glDrawArrays on AMD Hardware (Nvidia and Intel provide the correct result). Similar to the thread https://www.opengl.org/discussion_boards/showthread.php/200523-Vertex-shader-stops-passing-values-on-AMD-Hardware we are rendering 3-component colors with the type GL_UNSIGNED_BYTE. The problem is not restricted to drawing points, we also observe the same problem when rendering GL_TRIANGLES. The glDrawArrays call uses a non-zero offset (i.e. the first parameter in glDrawArrays(mode, first, count) is non zero). When rendering with such an offset, we observe flickering and/or that the last color in the color VBO is incorrectly black.

The problem only occurs with the following setup:

GL_VENDOR = ATI Technologies Inc.
GL_VERSION = 3.2.13521 Core Profile Context 24.20.11016.4
GL_RENDERER = Radeon ™ RX 480 Graphics

A simple test-program outputs the following images. The lower-left pixel is black in the failing case.

Correct vs Incorrect
[ATTACH=CONFIG]1870[/ATTACH] [ATTACH=CONFIG]1871[/ATTACH]

We have found two alternatives to avoid the problem:

[ol]
[li]Using glMultiDrawArrays which are OpenGL 1.4[/li][li]Using 4-component colors[/li][/ol]
The following simple test-program was used to narrow down the problem:

//------------------------------------------------------------------------------

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>

//------------------------------------------------------------------------------

static void error_callback(int, const char* description)
{
	fprintf(stderr, "Error: %s
", description);
}

//------------------------------------------------------------------------------

static void key_callback(GLFWwindow* window, int key, int, int action, int)
{
	if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
		glfwSetWindowShouldClose(window, GLFW_TRUE);
}

//------------------------------------------------------------------------------

/*
 * Based on:
 *	https://www.glfw.org/docs/latest/quick.html
 *	https://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_03
 *
 * Same bug see also:
 *	https://www.opengl.org/discussion_boards/showthread.php/200523-Vertex-shader-stops-passing-values-on-AMD-Hardware?p=1290870#post1290870
 */
int main(int argc, char **argv)
{

	GLFWwindow * window;

	glfwSetErrorCallback(error_callback);
	if (!glfwInit())
	{
		exit(EXIT_FAILURE);
	}
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	window = glfwCreateWindow(640, 480, "Rendering Test", NULL, NULL);
	if (!window)
	{
		glfwTerminate();
		exit(EXIT_FAILURE);
	}
	glfwSetKeyCallback(window, key_callback);
	glfwMakeContextCurrent(window);
	glfwSwapInterval(1);
	
	glewExperimental = GL_TRUE; // necessary on intel and nvidia
	if (glewInit() != GLEW_OK)
	{
		exit(EXIT_FAILURE);
	}

	std::cout << "GL_VENDOR = " << glGetString(GL_VENDOR) << std::endl;
	std::cout << "GL_VERSION = " << glGetString(GL_VERSION) << std::endl;
	std::cout << "GL_RENDERER = " << glGetString(GL_RENDERER) << std::endl;

	/*
	 * Loading shaders and geometry
	 */

	static const char* vertex_shader_text =
		"#version 150 core
"
		"in vec2 position;
"
		"in vec3 color;
"
		"out vec3 pass_color;
"
		"void main()
"
		"{
"
		"    gl_Position = vec4(position, 0.0, 1.0);
"
		"    pass_color = color;
"
		"}
";

	static const char* fragment_shader_text =
		"#version 150 core
"
		"in vec3 pass_color;
"
		"out vec4 out_color;
"
		"void main()
"
		"{
"
		"    out_color = vec4(pass_color, 1.0);
"
		"}
";

	float vertices[] = {
		 0.0f,  0.0f,
		 0.0f,  0.5f,
		 0.5f, -0.5f,
		-0.5f, -0.5f
	};

	/*
	 * The AMD driver bug does not occur if we use the same data type
	 * in the color array (GL_FLOAT) as in the vertex-position array.
	 *
	 * The problem also does not occur when using 4-component colors,
	 * this may be a hint that something related to data-structure 
	 * alignment may be the cause of the error.
	 */
	GLubyte colors[] =
	{
		255, 255, 255,	//255,
		255, 0, 0,		//255, 
		0, 255, 0,		//255, 
		0, 0, 255		//255  
	};

	GLuint vbo_position;
	glGenBuffers(1, &vbo_position);
	GLuint vbo_color;
	glGenBuffers(1, &vbo_color);

	glBindBuffer(GL_ARRAY_BUFFER, vbo_position);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	glBindBuffer(GL_ARRAY_BUFFER, vbo_color);
	glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);

	GLuint vao;
	glGenVertexArrays(1, &vao);

	/*
	 * Shader initialization
	 */
	GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(vertexShader, 1, &vertex_shader_text, NULL);
	glCompileShader(vertexShader);
	GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(fragmentShader, 1, &fragment_shader_text, NULL);
	glCompileShader(fragmentShader);
	GLuint shaderProgram = glCreateProgram();
	glAttachShader(shaderProgram, vertexShader);
	glAttachShader(shaderProgram, fragmentShader);
	glLinkProgram(shaderProgram);

	/*
	 * Get uniforms
	 */

	GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
	GLint colAttrib = glGetAttribLocation(shaderProgram, "color");

	/*
	 * VBO setup 
	 */

	glBindVertexArray(vao);

	glBindBuffer(GL_ARRAY_BUFFER, vbo_position);
	glEnableVertexAttribArray(posAttrib);
	glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);

	glBindBuffer(GL_ARRAY_BUFFER, vbo_color);
	glEnableVertexAttribArray(colAttrib);
	/*
	 * Only fails if we use GL_UNSIGNED_BYTE and 3 component colors.
	 */
	glVertexAttribPointer(colAttrib, 3, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);

	glBindVertexArray(0);
	
	while (!glfwWindowShouldClose(window))
	{
		int width, height;
		glfwGetFramebufferSize(window, &width, &height);
		float ratio = width / (float)height;

		glViewport(0, 0, width, height);
		glClearColor(0.5, 0.5, 0.5, 1.0);
		glClear(GL_COLOR_BUFFER_BIT);
		
		glUseProgram(shaderProgram);

		glPointSize(10.0f);
		glBindVertexArray(vao);

		/*
		 * Change flag to show the non-failing case
		 */
		bool failing_case = true;

		if (failing_case)
		{
			glDrawArrays(GL_POINTS, 1, 3); //GL_POINTS //GL_TRIANGLES
		}
		else
		{
			const GLint starts[] = { 1 };
			const GLsizei sizes[] = { 3 };
			glMultiDrawArrays(GL_POINTS, starts, sizes, 1);
		}

		glBindVertexArray(0);

		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	glDeleteProgram(shaderProgram);
	glDeleteShader(fragmentShader);
	glDeleteShader(vertexShader);

	glDeleteBuffers(1, &vbo_position);
	glDeleteBuffers(1, &vbo_color);

	glDeleteVertexArrays(1, &vao);

	glfwDestroyWindow(window);
	glfwTerminate();
	exit(EXIT_SUCCESS);

}

//------------------------------------------------------------------------------

Best,
Matthias

edit1: Added glewExperimental = GL_TRUE; to glew initialization in the test program to run the code on intel/nvidia and removed redundant VAO bind/unbind.

You’re going to be more likely to see this addressed if you submit a problem report to AMD:

http://www.amd.com/report

AMD has always had issues with misaligned VBO reads. Make sure that every attribute’s data is 4-byte aligned if you want it to work on their hardware. So colors have to be padded out to 4 bytes.

A way to troubleshoot these problems ahead of time is to use the Vulkan database and stick to formats that are supported for your hardware of interest. Vulkan actually requires implementations to specify which formats are allowed for which purposes, so you can see if a format is legal for a Vulkan implementation for the purposes of vertex fetching. For example, R8G8B8_UINT is not supported on any AMD hardware for VBO purposes. Indeed, AMD doesn’t support any non-4-byte-aligned format for buffer usage (or anything else, for that matter).

So don’t use an equivalent OpenGL vertex format.

Thank you for the very useful hints when supporting AMD hardware. I have resolved the problem by uploading 4-byte colors.

I may also write a bug report to AMD, although I currently seem to have problems accessing their report form.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.