Optimizing an OpenGL bitmap font render?


#1

Hello,

I have a basic (working) bitmap font renderer using C++/OpenGL in my program. I have a “Write” function which takes a const char*, and x/y pixel locations, and I ultimately end up with three things:

  1. A vertex array of vec4f’s, where the XY of the vec4f are vertex coords in screen space, and the ZW are the UV coords. So one quad for one character is four vec4f’s.

  2. An indices array of GLushorts.

  3. A count (a short) of how many text characters there are to draw this frame.

All three things are refilled/recalculated every frame, because the text being displayed can potentially change every frame, since some changing numbers are displayed, such as frame rate. I should also mention that the arrays are fixed. They’re normal arrays with fixed sizes on the stack, which may be relevant to my question.

So my question is, am I doing the following OpenGL code in the best way? I realize this is considered premature optimization, but this is more about me wanting to ensure I’m doing things correctly in general. So, after the vertex/index arrays are filled (on CPU), this is my OpenGL code, which is running every frame:

	// Bind VAO
	glBindVertexArray(_text_vao_id_hud);

	// Bind and set up VBO
	glBindBuffer(GL_ARRAY_BUFFER, _text_vbo_id_hud);
	glBufferData(GL_ARRAY_BUFFER,
		sizeof(VEC4F) * 4 * _text_character_count_hud,
		_text_character_vertices_hud, // This is a pointer (vertex array)
		GL_STATIC_DRAW);

	// Bind and set up EBO
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _text_ebo_id_hud);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER,
		sizeof(GLushort) * 6 * _text_character_count_hud,
		_text_character_indices_hud, // This is a pointer (indices array)
		GL_STATIC_DRAW);

	// Set up attrib array
	glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE,
		sizeof(VEC4F), (const GLvoid*)(0));
	glEnableVertexAttribArray(0);

	// Bind shader program
	glUseProgram(myFontShader);

	// Sets the uniform (texture ID)
	glUniform1i(_GetShaderUniform(UniformType::UNIFORM_TEXTURE_0), 0);
	glActiveTexture(GL_TEXTURE0);

	// Disable depth, so it paints on top of everything else
	glDisable(GL_DEPTH_TEST);

	// Bind and draw VAO with DrawElements
	glBindVertexArray(_text_vao_id_hud);
	glBindTexture(GL_TEXTURE_2D, _font_texture_id);
	glDrawElements(GL_TRIANGLES,
		6 * _text_character_count_hud,
		GL_UNSIGNED_SHORT,
		(const GLvoid*)(0));
	glBindVertexArray(0);

	// Re-enable depth
	glEnable(GL_DEPTH_TEST);

	// Unbind shader program
	glUseProgram(0);

Something tells me I don’t have to do all of this every frame. For example, I was wondering, since the arrays are static, that perhaps there’s some way to allocate fixed chunks on the GPU for those vertex/index arrays, and therefore simply update them every frame, but not keep re-allocating them? (I’m just guessing here if this would be even possible, let alone wise.)

Thank you for any advice!


#2

The second glBindVertexArray call is redundant. You shouldn’t need to call glVertexAttribPointer and glEnableVertexAttribArray each frame (that state is stored in the VAO), you shouldn’t need the glBindBuffer(GL_ELEMENT_ARRAY_BUFFER) call (that particular binding is also stored in the VAO), the glActiveTexture call appears to be redundant.

Repeated calls to glBufferData should be reasonably cheap provided that the size and usage remain unchanged, and safer than calling glBufferSubData as the latter risks implicit synchronisation. But the wiki recommends explicitly invalidating the previous contents by calling glBufferData with a null pointer first. Allocating an oversized buffer and using a different portion each frame reduces the reallocation rate, but that’s a relatively minor issue.

For more information, check the Buffer Object Streaming wiki page.


#3

Thanks for the help,

Since you’ve mentioned that re-allocation isn’t a big deal, I’ll leave them as glBufferData then, thanks. In my head, I had imagined glBufferData being somewhat like calling “new” so I was a bit concerned. : )

I’ve divided it into run-once and per-frame code, and commented out the right things (I think). Does this look right?

Run just once:

// Bind VAO
glBindVertexArray(_text_vao_id_hud);

// Set up attrib array
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE,
	sizeof(VEC4F), (const GLvoid*)(0));
glEnableVertexAttribArray(0);

// Unbind
glBindVertexArray(0);

Run every frame:

// Bind shader program
glUseProgram(myFontShader);

// Sets the uniform (texture ID)
glUniform1i(_GetShaderUniform(UniformType::UNIFORM_TEXTURE_0), 0);
//glActiveTexture(GL_TEXTURE0); // unnecessary

// Disable depth, so it paints on top of everything else
glDisable(GL_DEPTH_TEST);

// Bind VAO
glBindVertexArray(_text_vao_id_hud);

// Re-send vertex/index data
glBindBuffer(GL_ARRAY_BUFFER, _text_vbo_id_hud);
glBufferData(GL_ARRAY_BUFFER,
	sizeof(VEC4F) * 4 * _text_character_count_hud,
	_text_character_vertices_hud, // Pointer (vertex array)
	GL_STATIC_DRAW);
// glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _text_ebo_id_hud); // unnecessary?
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
	sizeof(GLushort) * 6 * _text_character_count_hud,
	_text_character_indices_hud, // Pointer (indices array)
	GL_STATIC_DRAW);

glBindTexture(GL_TEXTURE_2D, _font_texture_id);
glDrawElements(GL_TRIANGLES,
	6 * _text_character_count_hud,
	GL_UNSIGNED_SHORT,
	(const GLvoid*)(0));
glBindVertexArray(0);

// Re-enable depth
glEnable(GL_DEPTH_TEST);

// Unbind shader program
glUseProgram(0);

Thank you, again


#4

This should go into the “run just once” section. The GL_ELEMENT_ARRAY_BUFFER binding is stored in the active VAO.

Ah, I missed this in the first post. You need to use glActiveTexture prior to this if you can’t be sure of its current setting. But if you have enough texture units, you can just leave specific textures bound to specific units which avoids the need to rebind them. Default-block uniforms are stored in the program object so you don’t need to call glUniform1i each frame if the value doesn’t change.


#5

Thanks GClements,

Okay, so I understand now that GL_ELEMENT_ARRAY_BUFFER is a unique case and that it is indeed a member variable (of sorts) of the VAO, therefore it should be only bound once.

However, I’m still a bit hazy on VBO binding/relation to VAO, because I’d come across a few stackexchange answers which explain that the VAO does not have a VBO binding member(s) like it has for EBO, however, simultaneously they say that the VAO actually does remember the VBO binding, but only after you call glVertexAttribPointer() – whatever VBO was bound at that call, the VAO will remember – at least that is I think what they’re saying. So they’re also recommending to call glVertexAttribPointer(), only after binding a VBO in the first place.

Here is an example of a source that claims that:

https://gamedev.stackexchange.com/questions/99236/what-state-is-stored-in-an-opengl-vertex-array-object-vao-and-how-do-i-use-the

(The forum doesn’t want me posting links, but I think we can agree a stackexchange question is relevant and harmless)

Are they right? If they’re right, shouldn’t I do this in the “run once”?

glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo); //this one was missing before
glVertexAttribPointer(...);
glEnableVertexAttribArray(0);
glBindVertexArray(0);

And then in their draw code, they only recommend “Bind VAO + Draw” (But in my case, I would also need the two glBufferData calls, as the verts and indices data update each frame. So it’s been clarified that I don’t need the glBindBuffer(GL_ELEMENT_ARRAY_BUFFER) before the glBufferData for the indicies, since it’s actually a VAO member variable, but I’m uncertain about needing glBindBuffer(GL_ARRAY_BUFFER) for the VBO before the glBufferData call to update the verts data, since I’m still not sure if the VAO is supposed to inherently remember that VBO binding or not. Sorry if I’m beating a dead horse, I believe that was my last bit of confusion on this.

Thanks again for your time and help


#6

Prior to 3.0, attribute array state was part of the context. 3.0 added VAOs, and all state relating to attribute arrays is stored in them. In either case, the buffer associated with an attribute array is the buffer which was bound to GL_ARRAY_BUFFER at the time of the most recent glVertexAttribPointer call. That is the buffer from which the implementation will read attribute values when you call glDrawElements or similar. The current GL_ARRAY_BUFFER binding doesn’t affect draw calls (otherwise, you couldn’t use different buffers for different attributes).

The current GL_ARRAY_BUFFER binding isn’t part of the attribute array state and is never stored in a VAO. The current GL_ELEMENT_ARRAY_BUFFER binding is stored in a VAO (in versions which have VAOs). In the core profile (which doesn’t have a default VAO), it’s unclear whether you can even bind to GL_ELEMENT_ARRAY_BUFFER if no VAO is bound (the glBindBuffer documentation doesn’t mention this case explicitly, but the state tables don’t show any place for this state to be stored outside of a VAO).

Yes. If you’re sourcing attribute data from a VBO (required in 3.1+ core profile, optional in the compatibility profile), the buffer needs to be bound to GL_ARRAY_BUFFER at the time of the glVertexAttribPointer call. It doesn’t actually matter whether the VAO is bound when you call glBindBuffer(GL_ARRAY_BUFFER, ...), but both the VAO and VBO need to be bound for the glVertexAttribPointer call.


#7

Thank you so much. I failed to mention this before but yes I am using 3.1 (core?) as far as I know, since I wanted to ensure there were no compatibility problems with older systems, I went with that. My version initialize code is:

// Set OpenGL version
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
	SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

So now what I have is:

// Run once

glBindVertexArray(vao);

// Although this does not need to be bound after binding the vao,
// it does need to be bound before glVertexAttribPointer, so calling
// it right before it seems alright
glBindBuffer(GL_ARRAY_BUFFER, vbo);

// glVertexAttribPtr requires both the vao and GL_ARRAY_BUFFER
// to be bound, before calling.  Saves current GL_ARRAY_BUFFER
// binding into the active vao's state
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE,
	sizeof(VEC4F), (const GLvoid*)(0));
glEnableVertexAttribArray(0);

// For GL_ELEMENT_ARRAY_BUFFER, vao must be bound at the
// time of this call.  Saves current GL_ELEMENT_ARRAY_BUFFER
// into the vao state
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);

// unbind vao
glBindVertexArray(0);

// Unbind buffers?
// Is it safe to unbind both the GL_ARRAY_BUFFER and
// GL_ELEMENT_ARRAY_BUFFER here, after unbinding vao?
// I assume if I explicitly unbind them, they should still be successfully
// saved in the vao state yes?  Or, does unbinding vao unbind these anyway?
// Every frame

glBindVertexArray(vao);
glBufferData(GL_ARRAY_BUFFER, ...); //updates verts every frame
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ...); //updates indices every frame
glDrawElements(...);
glBindVertexArray(0);

Thank you GClements


#8

The GL_ELEMENT_ARRAY_BUFFER binding is saved in the VAO. Because of this, it’s unclear whether calling glBindBuffer(GL_ELEMENT_ARRAY_BUFFER) is valid when no VAO is bound. The current GL_ARRAY_BUFFER binding (i.e. what glGetIntegerv(GL_ARRAY_BUFFER_BINDING) returns) is stored in the context, not the VAO; it can be unbound immediately after glVertexAttribPointer returns. It isn’t necessary to unbind either of them. Unbinding the VAO will result in nothing being bound to GL_ELEMENT_ARRAY_BUFFER.

I’d suggest explicitly binding the buffer to GL_ARRAY_BUFFER before calling glBufferData. If you never bind anything else to that target, it isn’t strictly necessary to re-bind it, but it’s one less thing to cause problems as the program gets more complex. Well, you can bind it to any (non-indexed) binding target if you’re just updating the contents. The specific targets only matter for operations which have an implicit target, e.g. glVertexAttribPointer uses GL_ARRAY_BUFFER, glDrawElements uses GL_ELEMENT_ARRAY_BUFFER, etc. Also, GL_COPY_READ_BUFFER and GL_COPY_WRITE_BUFFER exist to allow a buffer to be bound for data transfers (glBufferData, glBufferSubData, glCopyBufferSubData, etc) without disturbing other binding points.


#9

Ahhh, okay. I had assumed that although glBindBuffer(GL_ARRAY_BUFFER …) doesn’t actually store that binding in the vao state on that call, (I had assumed) that once the glVertexAttribPtr function is called, that that binding is then and only then stored in the vao state. But that was wrong. So, the ARRAY_BUFFER binding is not stored in vao, even after glVertexAttribPtr – GL_ARRAY_BUFFER binding is only ever stored in the context – however it is, nevertheless, required to be bound at the time of the the glVertexAttribPtr call – got it.

This makes sense now, since I had previously assumed that binding the vao, also binds the ELEMENT_ARRAY_BUFFER and ARRAY_BUFFER bindings. (But now I understand that is only true for the former!) Since only the Element binding is loaded back up with the vao, it would be necessary then to explicitly bind the Array buffer again as you’ve said. (only confirming that I comprehend, here :slight_smile: )

Okay, now this makes sense to me, since a vao could potentially have utilized any number of different array buffers when being set up, there may only be one like in my case, or there may be three, etc.

So if I were hypothetically not updating the contents with glBufferData every frame, then I would not need that glBindBuffer(ARRAY_BUFFER) in the draw call.

I’ve learned a great deal just now. I’m a visual/hands-on learner so I normally learn from my mistakes by debugging and slowly stepping through code, Watch variables etc. This is tough when working with un-exposed code – at least for me. :slight_smile: Thank you, once again.


#10

No, that’s not what he was saying. You had the idea right the first time; the buffer object for an attribute is stored in the VAO upon calling glVertexAttrib*Pointer. What he’s saying is that the GL_ARRAY_BUFFER binding doesn’t matter after that call anymore. It’s a global variable that gets read from by glVertexAttrib*Pointer; after calling that function, its value becomes meaningless.

Or just use this API for doing VAO stuff. It’s much cleaner.


#11

Hmm, I see. In that case, would it be equally valid, then, to use some other function (assuming there is one) to directly retrieve the VBO binding from the vao itself, rather than manually specify it myself again in the call in Draw: glBindBuffer(GL_ARRAY_BUFFER, myVboVariable );

So, something like:

glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, glGetArrayBufferBindingFromCurrentVAO(0));
glBufferData(GL_ARRAY_BUFFER, ...);
// rest of Draw code

(Ignoring the fact that this would be slightly less than ideal since it would have one extra function call?)


#12

You can query the buffer associated with an attribute in the current VAO with glGetVertexAttribiv(GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING). There isn’t a mechanism to query this value for a specific (unbound) VAO; the other attribute properties can be queried with glGetVertexArrayIndexed, but not the buffer.

Historically, making glGet* calls within per-frame code was discouraged, as it caused a deep pipeline stall. It was preferable to maintain a shadow copy of the state in application memory and use that. I don’t know whether that’s still true to any significant extent.

Right, but that requires OpenGL 4.3. If you’re going to require that anyhow, then by all means use it. But personally I wouldn’t increase the version requirement just for that.


#13

Thank you guys so much for the help, things are a great deal more clear for me now. (And the render is of course working well)
: )