Handmade Hero»Forums»Code
107 posts
confused about glVertexAttribPointer Layout
Edited by C_Worm on Reason: Initial post
Hey I've have an Entitty struct that has:
4 v3f's for position.
4 v4f's for color.
4 v2f's for texCoords.

In main.cpp when i setup the VertexAttribPointers. the offset to where the colorattributes start seems wrong.

In my head the offset to the color attributes should be ( sizeof(float) * (2000 * 12) ), since there are 2000 entities and each has 4 v3f's
which is 4 * 3 = 12.

however this doesn't seem to align propelry because entity[0]'s color value only get color on 3 of 4 corners. the other entities get their color right though ( what i can see) .

If i change MAXPositionElements to 13 * MAXEntities it works or if i change all the MAX pos/col/tex to multiply by 32/32/32 instead of 12/16/8 it also works.
So why does it, I can't understand it .... I guess it has to do with the VertexAttrib layout but maybe the problem is in the UpdateAndDrawGLSubBuffer()??




defines.h
1
2
3
4
5
6
7
8
#define MAXEntities 		2000
#define MAXVBOIndices 		6 * MAXEntities

#define MAXPositionElements 12 * MAXEntities
#define MAXColorElements 	16 * MAXEntities
#define MAXTextureElements 	8 * MAXEntities

#define MAXTotalElements	(MAXPositionElements) + (MAXColorElements) + (MAXTextureElements)


main.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
struct OpenGLBufferObject
{
	unsigned int VAO;
	unsigned int VBO;
	unsigned int IBO;

	//unsigned int VBOpos;
	//unsigned int VBOcol;
	//unsigned int VBOtexCoord;
	
	void GenerateBuffers()
	{
		glGenVertexArrays(1, &VAO);
		glGenBuffers(1, &VBO);
		glGenBuffers(1, &IBO);
	}

	
	void AllocateVBO(unsigned int size)
	{
		glBindBuffer(GL_ARRAY_BUFFER, VBO);
		glBufferData(GL_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW); 
	}

	void SetupVBOVertexAttrib(unsigned int offsetToColorAttributes, unsigned int offsetToTextureAttributes)
	{
		glEnableVertexAttribArray(0);
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, (void*)0);

		glEnableVertexAttribArray(1);
		glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)(offsetToColorAttributes));
				
		glEnableVertexAttribArray(2);
		glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, (void*)((offsetToColorAttributes + offsetToTextureAttributes)));
	}

	void SetupIBOVertexAttrib(unsigned int sizeOfVertexIndices, unsigned int addresOfVertexIndices[])
	{
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeOfVertexIndices, addresOfVertexIndices, GL_STATIC_DRAW);
	}

	void UpdatePosData(unsigned int offset, unsigned int sizeOfUpdateData, v3f addresOfDataToUpdate[])
	{
		glBufferSubData(GL_ARRAY_BUFFER, offset, sizeOfUpdateData, addresOfDataToUpdate);
	}

	void UpdateColData(unsigned int offset, unsigned int sizeOfUpdateData, v4f addresOfDataToUpdate[])
	{
		glBufferSubData(GL_ARRAY_BUFFER, offset, sizeOfUpdateData, addresOfDataToUpdate);
	}

	void BindVAO()
	{
		glBindVertexArray(VAO);
	}
	void BindVBO()
	{
		glBindBuffer(GL_ARRAY_BUFFER, VBO);
	}
	void BindIBO()
	{
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
	}
	void DeleteBuffers()
	{

		glGenVertexArrays(1, &VAO);
		glDeleteBuffers(1, &VBO);
		glDeleteBuffers(1, &IBO);
	}
};


main.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
int main()
{
	for(int i = 0; i < MAXEntities; i++)
	{
		vertexIndex[(i * 6) + 0] = (i * 4) + 0;
		vertexIndex[(i * 6) + 1] = (i * 4) + 1;
		vertexIndex[(i * 6) + 2] = (i * 4) + 2;
		vertexIndex[(i * 6) + 3] = (i * 4) + 1;
		vertexIndex[(i * 6) + 4] = (i * 4) + 2;
		vertexIndex[(i * 6) + 5] = (i * 4) + 3;
	}

	OpenGLBufferObject glBuffobj = {};
	glBuffobj.GenerateBuffers();
	glBuffobj.BindVAO();
	unsigned int posMemory = sizeof(float) * (2000 * 12);
	unsigned int colMemory = sizeof(float) * (2000 * 16);
	unsigned int texMemory = sizeof(float) * (2000 * 8);
	unsigned int totalMemory = posMemory + colMemory + texMemory;
	glBuffobj.AllocateVBO(totalMemory);
	glBuffobj.SetupVBOVertexAttrib(sizeof(float) * (MAXPositionElements), sizeof(float) * (MAXColorElements));
	glBuffobj.SetupIBOVertexAttrib(sizeof(vertexIndex), vertexIndex);


	GameState gameState = {};
	Entity entity[MAXEntities] = {};
	Input input = {};
	Color4f clearColor = {};

	gameState.running = true;

	entity[PLAYER1].Init	( 0.0f, 0.0f, 0.1f, 0.1f);
	entity[PLAYER2].Init	( 0.0f, 0.0f, 0.1f, 0.1f);
	entity[2].Init	        ( 0.0f, 0.0f, 0.1f, 0.1f);
	entity[3].Init		(-0.5f, 0.1f, 0.1f, 0.1f);
	entity[4].Init		(-0.8f, 0.1f, 0.1f, 0.1f);
	entity[5].Init		( 0.8f, 0.1f, 0.1f, 0.1f);

	entity[PLAYER1].SetSolidColor	(1.0, 0.0f, 1.0f, 1.0f);
	entity[PLAYER2].SetSolidColor	(0.0, 1.0f, 0.0f, 1.0f);
	entity[2].SetSolidColor		(0.0, 0.0f, 1.0f, 1.0f);
	entity[3].SetSolidColor		(1.0, 0.0f, 0.0f, 1.0f);
	entity[4].SetSolidColor		(0.5, 0.0f, 1.0f, 1.0f);


       while(running)
       {
	        glClear(GL_COLOR_BUFFER_BIT);

		gameCodeDLL.UpdateGameCode(&gameState, entity, &input, &clearColor);

		glBuffobj.BindVBO();
		UpdateAndDrawGLSubBuffer(entity, 0, MAXEntities);

		glfwSwapBuffers(window);
		glfwPollEvents()

       }
}

void UpdateAndDrawGLSubBuffer(Entity *_entityBuffer, unsigned int _startOffset, unsigned int _lastOffset)
{
	if(!(_startOffset > _lastOffset))
	{
		unsigned int startVerts_Pos = 0;
		unsigned int startVerts_Col = sizeof(float) * MAXPositionElements;

		unsigned int entityIndex_Pos = 0;
		unsigned int entityIndex_Col = 0;
		size_t sizeUpdate_Pos = 4 * sizeof(v3f);
		size_t sizeUpdate_Col = 4 * sizeof(v4f);

		for(int startOffset = _startOffset; 
			startOffset <= _lastOffset; 
			startOffset++)
		{
			entityIndex_Pos = startOffset * (4 * sizeof(v3f));
			entityIndex_Col = startOffset * (4 * sizeof(v4f));

			glBufferSubData(GL_ARRAY_BUFFER, startVerts_Pos + entityIndex_Pos, sizeUpdate_Pos, _entityBuffer[startOffset].posVertex);   
			glBufferSubData(GL_ARRAY_BUFFER, startVerts_Col + entityIndex_Col, sizeUpdate_Col, _entityBuffer[startOffset].colVertex);   
		}


		glDrawElements(GL_TRIANGLES, 6 + (6 * _lastOffset), GL_UNSIGNED_INT, (unsigned int*)((sizeof(float) * 6) * _startOffset));
	}
	else
	{
		printf("startOffset Can't be bigger than lastOffset!!\n");

	}
} 



GameCode.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
struct Entity
{
	unsigned int sizePos;
	unsigned int sizeCol;
	unsigned int sizeTex;
	unsigned int arrayIndex;

	bool initialized = false;

	v2f pos;
	v2f dimension;
	v2f velocity;

	v3f posVertex[4];
	v4f colVertex[4];
	v2f texCoordVertex[4];

	void Init(float _x, float _y, float _w, float _h)
	{
		if(!initialized)
		{
			pos.x 		= _x;
			pos.y 		= _y;
			dimension.x 	= _w;
			dimension.y 	= _h;
			

			posVertex[0].x 	= _x;
			posVertex[0].y 	= _y;

			posVertex[1].x 	= _x;
			posVertex[1].y 	= _y + _h;

			posVertex[2].x 	= _x + _w;
			posVertex[2].y 	= _y;

			posVertex[3].x 	= _x + _w;
			posVertex[3].y 	= _y + _h;

			initialized = true;
		}


	}

	void SetVelocity(v2f _vel)
	{
		velocity.x = _vel.x;
		velocity.y = _vel.y;
	}
	
	void UpdatePosition()
	{
		posVertex[0].x  = pos.x;
		posVertex[0].y  = pos.y;

		posVertex[1].x  = pos.x;
		posVertex[1].y  = pos.y + dimension.y;

		posVertex[2].x  = pos.x + dimension.x;
		posVertex[2].y  = pos.y;
		
		posVertex[3].x 	= pos.x + dimension.x;
		posVertex[3].y 	= pos.y + dimension.y;
	}

	void SetSolidColor(float _r, float _g, float _b, float _a)
	{
		colVertex[0].x = _r;
		colVertex[0].y = _g;
		colVertex[0].z = _b;
		colVertex[0].w = _a;

		colVertex[1].x = _r;
		colVertex[1].y = _g;
		colVertex[1].z = _b;
		colVertex[1].w = _a;

		colVertex[2].x = _r;
		colVertex[2].y = _g;
		colVertex[2].z = _b;
		colVertex[2].w = _a;

		colVertex[3].x = _r;
		colVertex[3].y = _g;
		colVertex[3].z = _b;
		colVertex[3].w = _a;
	}
};
Mārtiņš Možeiko
2559 posts / 2 projects
confused about glVertexAttribPointer Layout
Edited by Mārtiņš Možeiko on
I think you are simply overwriting your color memory in UpdateAndDrawGLSubBuffer function when you are updating position of last entity.
You are calling this function with start=0, last=MAXEntities arguments. And your for loop is constructed to use [start, last] indices inclusively. That means it will update MAXEntities+1 elements in the array. But there is only space for MAXEntities elements. Thus the last one element of position memory will overwrite first element of color memory.

The fix would be to change call to UpdateAndDrawGLSubBuffer(entity, 0, MAXEntities-1);

Btw, GPUs prefers tightly packed vertex information - due to cache reasons:
1
2
3
4
5
6
struct Vertex
{
	v3f posVertex;
	v4f colVertex;
	v2f texCoordVertex;
};

Basically use array of structures, not separate arrays for vertex information.

Another optimization would be to reduce memory size needed by color component - instead of v4f use just one uint32. This will reduce vertex size from 36 bytes to 24 bytes. Pretty significant savings.

Most important optimization would be to avoid calling glBufferSubData so many times (once per entity). This wastes performance a lot. Instead you should call it as little as possible. Alternative is to use glMapBuffer/glUnmapBuffer to get pointer where to write, then small writes will be ok.

107 posts
confused about glVertexAttribPointer Layout
thanks!

you're the best!