Advertisement

Render 2D text for screen

Started by April 25, 2020 01:57 PM
6 comments, last by Range 4 years, 9 months ago

Hello! I am making a small font engine for draw statistics on screen. My example:

My system is worked so: 

  1.  I create dynamic vertex buffer with size float3 (position) x float2 (uv coordinates) x 1000
  2.  Start game loop
  3.  For every frame I map vertex buffer (font engine)
  4.  I update necessary vertex (fps etc)
  5.  Unmap vertex buffer (font engine)
  6.  Draw vertex buffer (font engine) (1 draw call)

I am kind of a beginner (many optimization concepts are not familiar to me), so I wanted to know:

  1. Is this way of drawing text effective? After all, the constant display of memory, if I'm not mistaken, is very expensive. For example OpenGL prints warning:   

Debug message (131186): Buffer performance warning: Buffer object 1 (bound to GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB (0), GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB (1), and GL_ARRAY_BUFFER_ARB, usage hint is GL_DYNAMIC_DRAW) is being copied / moved from VIDEO memory to DMA CACHED memory.

Source: API

Type: Performance

Severity: medium

      2. I noticed that when I switch to full screen, I have FPS losses (~ 150 - 200). Is this normal practice, or am I incorrectly calling screen mode switching methods?

Big thanks!

P.S.: graphics api that i use: d3d11, opengl 4.2 - 4.6(new context).

 

1. If even a computer complaints about performance, it's likely not efficient :p

The usual tactic is to make a texture with all possible glyphs you may want to draw. At the CPU you know the coordinates to use for getting a glyph. For painting, you send coordinates to draw glyphs from the texture to the appropriate position at the screen so it looks like you're printing text. The challenge here is of course kerning.

2. FPS loss numbers don't have fixed meaning, 100 loss is nothing if you have 2000 fps, and a nightmare if you have 101 fps. In other words, it's impossible to judge if you have a problem from your question. Don't use fps for measuring performance, use absolute time, probably in mill-seconds or so.

Advertisement

I don't think this is normal.

I have a similar thing, but just OpenGL 4.5. I build a texture atlas for a font of a size, then use a static buffer for the labels and a dynamic buffer for the variable things. The static buffer is filled when the UI elements are defined, the dynamic buffer is mapped every frame, I obtain a pointer to the buffer, update its contents and unmap it. There are no performance warnings, nothing is being copied, and i don't realise any performance impact.

@Alberth @Green_Baron Thank tou for answer! I use texture atlas technology with glyphs. The vertex buffer stores 4 vertices where there is a position and texture coordinates in the atlas. 4 vertices are used because I draw using the triangle_strip. During the update, I display the vertex buffer, replace some of them, and perform anmap. Is it really so hard on performance? My system is almost completely similar to your...

P.S.: @Alberth about FPS thanks, everything is clear!

GPU is fast, but the databus between GPU and memory/CPU isn't. You push new textures through that small hole every frame. (Not sure of the sizes and data formats used, but let's say RGBA 8bit channels, at 300x200? 4 bytes x 300 x 200 = 240000 bytes or 240KB every frame. You can push 60K floats in that space.)

The difference between your approach and what is usually done, is that normally, you have 4 vertices for each letter rather than 4 vertices for lots of dynamic text. Those vertices can change dynamically easily, pushing a few position floats through the small hole isn't a problem. Your approach works very well for static text, where you can upload the texture once.

Yes, a texture atlas only needs one channel, assuming one just writes text. Texture size depends on font size. It can stay bound to the shader pipeline and the render area limited by scissors. So the only state change would be blending, scissors, and the shader and vertex array switch. Not a big effort.

I could spare the texture state change with choosing a unit other than 0 so it doesn't conflict with other shaders/textures. WIP :-) (https://github.com/BaronVerde/VeryVerySimpleGUI)

void gui_window_render( gui_window_t* w, const vec3f* color ) {
	// just a shortcut
	gui_window_internals_t* i = w->internals;
	// @todo glScissor()
	glEnable( GL_BLEND );
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
	glUseProgram( shader_program );
	// @todo choose a different unit
	glBindTextureUnit( 0, w->font->texture_atlas );
	// @todo only on color change
	glUniform3f( glGetUniformLocation( shader_program, "pen_color" ), color->x, color->y, color->z );	
	glBindVertexArray( i->vertex_array );
	// draw the static elements
	glVertexArrayVertexBuffer( i->vertex_array, 0, i->static_vertex_buffer, 0, sizeof( vec4f ) );
	glDrawArrays( GL_TRIANGLES, 0, i->num_static_vertices );
	// draw the flickering variables
	glVertexArrayVertexBuffer( i->vertex_array, 0, i->dynamic_vertex_buffer, 0, sizeof( vec4f ) );
	glDrawArrays( GL_TRIANGLES, 0, i->num_dynamic_vertices );
}
Advertisement

@Alberth @Green_Baron

Thanks for your advice! Especially about the bus between CPU and GPU. I edit my code all day, and as a result I optimally update small vertex blocks. The warning has disappeared. Thanks again!

This topic is closed to new replies.

Advertisement