Handmade Hero»Forums»Code
100 posts
OpenGL: Is VAO creation expensive?
Replying to mmozeiko (#29717)

Constants:

// attribute index in GLSL shader const GLint a_pos = 0; const GLint a_texcoords = 1; const GLint a_normals = 2;

// vertex buffer index used in setup & draw calls const GLuint vbuf_index_pos = 0; const GLuint vbuf_index_texcoords = 1; const GLuint vbuf_index_normals = 2;

Format setup:

GLuint vao; glCreateVertexArrays(1, &vao);

glVertexArrayAttribFormat(vao, a_pos, 3, GL_FLOAT, GL_FALSE, 0); glVertexArrayAttribBinding(vao, a_pos, vbuf_index_pos); glEnableVertexArrayAttrib(vao, a_pos);

glVertexArrayAttribFormat(vao, a_texcoords, 2, GL_FLOAT, GL_FALSE, 0); glVertexArrayAttribBinding(vao, a_texcoords, vbuf_index_texcoords); glEnableVertexArrayAttrib(vao, a_texcoords);

glVertexArrayAttribFormat(vao, a_normals, 3, GL_FLOAT, GL_FALSE, 0); glVertexArrayAttribBinding(vao, a_normals, vbuf_index_normals); glEnableVertexArrayAttrib(vao, a_normals);

mesh setup:

GLuint vbuf_pos; GLuint vbuf_texcoord; GLuint vbuf_normals; GLuint ibuf;

// can create all of them with one call, just pass array of 4 glCreateBuffers(1, &vbuf_pos); glCreateBuffers(1, &vbuf_texcoord); glCreateBuffers(1, &vbuf_normals); glCreateBuffers(1, &ibuf);

glNamedBufferStorage(vbuf_pos, points.size()*sizeof(points[0]), points.data()); glNamedBufferStorage(vbuf_texcoord, texcoords.size()*sizeof(texcoords[0]), texcoords.data()); glNamedBufferStorage(vbuf_normals, normals.size()*sizeof(normals[0]), normals.data()); glNamedBufferStorage(ibuf, indexes.size()*sizeof(indexes[0]), indexes.data());

draw call:

// do it once glBindVertexArray(format.vao);

// for each mesh do: glVertexArrayVertexBuffer(format.vao, vbuf_index_pos, mesh.vbuf_pos, 0, sizeof(PointStruct)); glVertexArrayVertexBuffer(format.vao, vbuf_index_texcoords, mesh.vbuf_texcoord, 0, sizeof(TexcoordStruct)); glVertexArrayVertexBuffer(format.vao, vbuf_index_normals, mesh.vbuf_normals, 0, sizeof(NormalStruct)); glVertexArrayElementBuffer(format.vao, mesh.ibuf); glDrawElements(GL_TRIANGLES, mesh.vertex_count, GL_UNSIGNED_BYTE, NULL);

This code will require at least GL 4.5 version. Or ARB_direct_state_access extension presence).

Be aware that interleaved attributes in one buffer will be better for performance.

Ok so now you edited the previous answer and gave code, thanks. :)

I'll see if I can use OpenGL 4.5, but I think mine is 4.1.

Is there any real performance penalty or otherwise in using OpenGL 4.1 and older API?

Thanks.

Mārtiņš Možeiko
2568 posts / 2 projects
OpenGL: Is VAO creation expensive?
Replying to da447m (#29730)

I want to believe you, but my suspicion is that there is something else in your code that triggers this problem. I guarantee you 100% that OpenGL does not require to unbind VAO for this to work.

Is there any real performance penalty or otherwise in using OpenGL 4.1 and older API?

It's not that there is performance penalty, but you often can do things simpler and decrease CPU overhead.

Separable vertex attribs is 4.3 version thing anyway, so 4.5/DSA requirement does not matter if you cannot use separable vertex attribs (ARB_vertex_attrib_binding extension).

100 posts
OpenGL: Is VAO creation expensive?
Replying to mmozeiko (#29732)

Guess you are right, I changed the code to pack the data together and use a single VBO, and voilá, now I do not need to unbind it anymore.

Thanks!

This is now the complete flux with the data packing you suggested (point next to normal next to texture coord). It got ugly with the vector but I do not want to use struct padding stuff for now, I'm still doing models by hand to learn.

It also lets me more clearly generate the normals from the points and indexes into a separate vector. I figured a way to do that and now all my lights are correct with the 5 platonic solids.

void Model::generate(const V_Float& points,
					 const V_Float& texcoords,
					 const V_Byte& indexes,
					 const V_Float& normals)
{
	/// OBS: pay attention to order, we make an huge single vector so we just
	/// committ is all to a single VBO, then pass the proper pointer offsets to
	/// glVertexAttribPointer, and make sure layouts match in teh shader code
	///
	V_Float vdata;

	size_t i = 0, j = 0;
	while (1)
	{
		vdata.push_back(points[i]);
		vdata.push_back(points[i+1]);
		vdata.push_back(points[i+2]);

		vdata.push_back(normals[i]);
		vdata.push_back(normals[i+1]);
		vdata.push_back(normals[i+2]);

		vdata.push_back(texcoords[j]);
		vdata.push_back(texcoords[j+1]);

		i += 3;
		j += 2;

		if (i >= points.size() || i >= normals.size() || j >= texcoords.size() )
		{
			break;
		}
	}

	glGenVertexArrays( 1, &m_VAO );
	glBindVertexArray( m_VAO );

	glGenBuffers( 1, &m_VBO );
	glBindBuffer( GL_ARRAY_BUFFER, m_VBO );

	glBufferData( GL_ARRAY_BUFFER,
				 vdata.size()*sizeof(vdata[0]),
				 vdata.data(),
				 GL_STATIC_DRAW );

	glGenBuffers( 1, &m_EBO );
	glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_EBO );

	glBufferData( GL_ELEMENT_ARRAY_BUFFER, indexes.size()*sizeof(indexes[0]), indexes.data(), GL_STATIC_DRAW );

	size_t VBO_chunk_size = 3*sizeof(GLfloat) + 3*sizeof(GLfloat) + 2*sizeof(GLfloat);

	/// points
	glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, VBO_chunk_size, (void*)0 );
	/// normals
	glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, VBO_chunk_size, (void*)(3*sizeof(GLfloat)) );
	/// texture coordinates
	glVertexAttribPointer( 2, 2, GL_FLOAT, GL_FALSE, VBO_chunk_size, (void*)(3*sizeof(GLfloat)+3*sizeof(GLfloat)) );

	/// points and texture coords have to match the layout (location = x) in the shader
	/// programms, i.e. if the index for points is 0, it will be passed to the shader for
	/// the location = 0
	glEnableVertexAttribArray( 0 );
	glEnableVertexAttribArray( 1 );
	glEnableVertexAttribArray( 2 );
}
100 posts
OpenGL: Is VAO creation expensive?
Replying to da447m (#29734)

Result so far :)

2023-11-06 18-43-16-1.m4v