OpenGL Issues

Inconsistencies I encountered during OpenGL and OpenGL ES dev.

--by Captdam @ 2023-05-01 20:03 GMT

OpenGL (GL) and OpengGL ES (GLES) only specify what the driver and hardware should behaviour but not what should be done in low level and what shoul not be done, this causes lots of inconsistencies between hardware and software. During my development with OpenGL and OpenGL ES, I had encounterd some issues that the program doesn't do the same as the specifiction said or does different on different platform.

Most of the issues have been discussed online by others, and some of them has been fixed in new dirver. Here I am listing issues I encountered but has not seen anyone post them online (or it is hard to find a thread talking about them). Hope this can help saving time for some other poor guy like me.

I am still actively adding new entries.

textureOffset() and other *Offset() functions

RPi 4B / Buster / OpenGL ES 3.1

In GLSL (shader language), the textureOffset(sampler tex, vecN pos, ivecN offset) and similar functions allow us to fetch a pixel that is offset by exact offset pixels from the pos. This is useful for something like a filter. Following is an example for 2D texture:

	vec4 color = texture(tex, pos) * 0.5;
	color += textureOffset(tex, pos, ivec2(-1,0)) * 0.125;
	color += textureOffset(tex, pos, ivec2(+1,0)) * 0.125;
	color += textureOffset(tex, pos, ivec2(0,-1)) * 0.125;
	color += textureOffset(tex, pos, ivec2(0,+1)) * 0.125;

The above example runs without issue. It may seems reasonable to use the offset dynamically (for example, the offset array is saved in another texture):

	vec4 color = texture(tex, pos) * 0.5;
	for (int i = 0; i < 20; i++) {
		color += textureOffset(tex, pos, offset[i]) * weight[i];

The above example runs without issue on my desktop, but cannot be compiled on my RPi.

I cannot find any where document this restriction, neither in the reference nor the specification. Luckily, we can wrap this function easily:

	vec4 color = texture(tex, pos) * 0.5;
	for (int i = 0; i < 20; i++) {
		vec2 p = calculatePos(pos, offset[i]); //Use textureSize() internally
		color += texture(tex, pos) * weight[i];

VAO Binding index (binding index for buffer, not the attribute index)

Ubuntu20.04.3LTS / RTX2080S Version 4.6.0 NVidia 470.182.03

I encountered this issue when I migrate my old lib from GLES3.1 to GL4.6 to enjoy the convenience of DSA (direct state access). Prior to GL4.3, we will need to use glVertexAttribPointer() to bind a buffer to the VAO and setup a attribute (layout in GLSL) to a specific offset in the buffer. Since GL4.3, this operation is separated into two different operations: glVertexAttribFormat() (glVertexArrayAttribFormat() in GL4.5 for DSA) and glVertexAttribBinding() (glVertexArrayAttribBinding() in GL4.5 for DSA).

To my understanding (correct me if I am wrong), a VAO will have a list of binding points and a buffer can be bind to. The glVertexAttribFormat() call set an attribute (layout in GLSL) to that binding point; the glVertexAttribBinding() call then bind a buffer to that binding box.

There are a number of examples showing how to use this new feature with only one buffer (VBO) bind to the VAO (some also with a EBO bound), and they all work on my machine. However, things get tricky when I bind two buffers on the same VAO. Following is an simplified code of my project:

	#define BIND_VBO 0
	#define BIND_IBO 1
	glCreateVertexArrays(1, &mesh.vao);
	__gl_log("Create VAO = %u VBO = %u EBO = %u IBO= %u", mesh.vao, mesh.vbo, mesh.ebo, mesh.ibo);

	glVertexArrayVertexBuffer(mesh.vao, BIND_VBO, mesh.vbo, 0, 5 * sizeof(float)); //VBO at bindingindex 0
	glEnableVertexArrayAttrib(mesh.vao, 0); //layout 0 position
	glVertexArrayAttribFormat(mesh.vao, 0, 3, GL_FLOAT, GL_FALSE, 0);
	glVertexArrayAttribBinding(mesh.vao, 0, BIND_VBO);
	glEnableVertexArrayAttrib(mesh.vao, 1); //layout 1 texture coord
	glVertexArrayAttribFormat(mesh.vao, 1, 2, GL_FLOAT, GL_FALSE, 3 * sizeof(flaot));
	glVertexArrayAttribBinding(mesh.vao, 1, BIND_VBO);

	glVertexArrayVertexBuffer(mesh.vao, BIND_IBO, mesh.vbo, 0, 3 * sizeof(float)); //IBO at bindingindex 1
	glEnableVertexArrayAttrib(mesh.vao, 3); //layout 3 position
	glVertexArrayAttribFormat(mesh.vao, 3, 3, GL_FLOAT, GL_FALSE, 0);
	glVertexArrayBindingDivisor(mesh.vao, 3, 1);
	glVertexArrayAttribBinding(mesh.vao, 3, BIND_IBO);

No error reported; however,the graphic result is bad. After hours of debug and guess, found out that the binding index of the new IBO must be an unused attribute index. In another word, change #define BIND_IBO to 2.

In the reference, it only states "glVertexAttribBinding and glVertexArrayAttribBinding establishes an association between the generic vertex attribute of a vertex array object whose index is given by attribindex, and a vertex buffer binding whose index is given by bindingindex." and "glBindVertexBuffer and glVertexArrayVertexBuffer bind the buffer named buffer to the vertex buffer binding point whose index is given by bindingindex". No simular restriction is found in the specifiction neither. That means, the arrtibute index and the binding index are two unrelated index. As long as different buffers are bind on different binding indices, and different attributes are bind on different attribute indices, there should be no problem. However, on my machine, I must make sure the binding index of a specific buffer is not same as attribute indices used by other buffers.