Allegro.cc - Online Community

Allegro.cc Forums » Allegro Development » Slowness when drawing fonts

This thread is locked; no one can reply to it. rss feed Print
 1   2   3   4 
Slowness when drawing fonts
AMCerasoli
Member #11,955
May 2010
avatar

Hi guys, I'm noticing an incredible slowness when drawing fonts.

I'm initializing a font on a second thread to avoid having to initialize it when the object is created, since it's slow and I can notice my game getting stuck a little bit. But, after initialize it when I draw it for first time, I still noticing my game getting stuck, even if I just draw one letter. Once the text has been drawn for first time, the slowness disappear... I just wanted to know if this is normal, or there is a problem.

Todd Cope
Member #998
November 2000
avatar

When using TrueType fonts, glyphs are cached when they are drawn for the first time. You should expect this initial slowness as a result.

Mark Oates
Member #1,146
March 2001
avatar

Hmm, why are they not cached when the font is loaded?

Todd Cope
Member #998
November 2000
avatar

Probably because caching thousands of unicode characters is not feasible. I think there should be an al_cache_font_glyph_ranges(ALLEGRO_FONT * fp, int ranges_n, int ranges[]) function. If you know what characters you are going to be using, this would be quite useful.

Mark Oates
Member #1,146
March 2001
avatar

Ah, so it just caches those characters you're using when you first draw them.

AMCerasoli
Member #11,955
May 2010
avatar

Yes I think they should be a way to "prepare" the font within the loading process? doesn't matter how long it takes, I don't thin would take too much time anyway... I would prefer that than this...

I did this little example to see if there is something wrong with my game. But the problem still there.

example.zip

if you want the .exe download this

Once I draw the font for first time the velocity increase. Here I loaded the same font 5 times, so you can check it more than just once before closing the application. And I'm playing a beep continuously so you can hear the interruption when the font is drawn.

I also put an if to draw 10 bitmaps at the same time, to prove that drawing fonts for first time is ridiculously slow.

Key A, S, D, F and G draw text. Key B draw 10 bitmaps.

I didn't destroy the objects, anyway it's just an example...

#SelectExpand
1#include <stdio.h> 2#include <list> 3 4#define ALLEGRO_STATICLINK 5#include "allegro5/allegro.h" 6#include "allegro5/allegro_font.h" 7#include "allegro5/allegro_ttf.h" 8#include "allegro5/allegro_image.h" 9#include <allegro5/allegro_audio.h> 10#include <allegro5/allegro_acodec.h> 11 12class SPOT { 13 14 private: 15 16 ALLEGRO_BITMAP *bmp; 17 float posi_X, posi_Y, vel; 18 19 public: 20 21 SPOT(ALLEGRO_BITMAP *T_bmp, float T_y, float T_vel) : 22 bmp (T_bmp), 23 posi_X (-100), 24 posi_Y (T_y), 25 vel (T_vel){} 26 27 ~SPOT(){ 28 29 al_destroy_bitmap(bmp); 30 31 } 32 33 void move(){ 34 35 al_draw_bitmap(bmp, posi_X, posi_Y, 0); 36 37 posi_X += vel; 38 39 if(posi_X>640) posi_X=-100; 40 41 } 42 43}; 44 45int main() 46{ 47 if(!al_init()) 48 { 49 fprintf(stderr, "Failed to initialize Allegro.\n"); 50 return -1; 51 } 52 53 al_init_font_addon(); 54 al_init_ttf_addon(); 55 al_init_image_addon(); 56 al_install_audio(); 57 al_init_acodec_addon(); 58 al_reserve_samples(1); 59 al_install_keyboard(); 60 61 ALLEGRO_DISPLAY *display = NULL; 62 display = al_create_display(640,480); 63 ALLEGRO_BITMAP *Obj_Bmp = al_load_bitmap("spot.png"); 64 ALLEGRO_BITMAP *Obj_Bmp2= al_load_bitmap("spot.png"); 65 ALLEGRO_FONT *font = al_load_ttf_font("consola.ttf",72,0 ); 66 ALLEGRO_FONT *font2 = al_load_ttf_font("consola.ttf",72,0 ); 67 ALLEGRO_FONT *font3 = al_load_ttf_font("consola.ttf",72,0 ); 68 ALLEGRO_FONT *font4 = al_load_ttf_font("consola.ttf",72,0 ); 69 ALLEGRO_FONT *font5 = al_load_ttf_font("consola.ttf",72,0 ); 70 ALLEGRO_KEYBOARD_STATE keyboard; 71 ALLEGRO_SAMPLE *beep = al_load_sample("beep.ogg"); 72 73 //Obj_Bmp = al_load_bitmap("spot.png"); 74 75 76 std::list<SPOT*> Spo_List; 77 78 for(int a=0; a<10; a++) 79 Spo_List.push_back(new SPOT(Obj_Bmp, rand()%480, rand()%10+1)); 80 81 while(1){ 82 al_get_keyboard_state(&keyboard); 83 84 al_clear_to_color(al_map_rgb(50,10,70)); 85 86 if(al_key_down(&keyboard, ALLEGRO_KEY_A)) 87 al_draw_text(font, al_map_rgba(255,255,255,255), 640/2, (480/4),ALLEGRO_ALIGN_CENTRE, "Slow Font : (!"); 88 89 else if(al_key_down(&keyboard, ALLEGRO_KEY_S)) 90 al_draw_text(font2, al_map_rgba(255,255,255,255), 640/2, 50+(480/4),ALLEGRO_ALIGN_CENTRE, "Slow Font : (!"); 91 92 else if(al_key_down(&keyboard, ALLEGRO_KEY_D)) 93 al_draw_text(font3, al_map_rgba(255,255,255,255), 640/2, 100+(480/4),ALLEGRO_ALIGN_CENTRE, "Slow Font : (!"); 94 95 else if(al_key_down(&keyboard, ALLEGRO_KEY_F)) 96 al_draw_text(font4, al_map_rgba(255,255,255,255), 640/2, 150+(480/4),ALLEGRO_ALIGN_CENTRE, "Slow Font : (!"); 97 98 else if(al_key_down(&keyboard, ALLEGRO_KEY_G)) 99 al_draw_text(font5, al_map_rgba(255,255,255,255), 640/2, 200+(480/4),ALLEGRO_ALIGN_CENTRE, "Slow Font : (!"); 100 101 else if(al_key_down(&keyboard, ALLEGRO_KEY_B)) 102 for(int a=0; a<10;a++) 103 al_draw_bitmap(Obj_Bmp2, 200+rand()%40, 200+rand()%40, 0); // This Bitmap haven't being drawn previusly. 104 105 else if(al_key_down(&keyboard, ALLEGRO_KEY_ESCAPE)) 106 break; 107 108 for(std::list<SPOT*>::iterator it = Spo_List.begin(); it != Spo_List.end(); it++) 109 (*it)->move(); 110 111 al_flip_display(); 112 113 al_rest(0.05); 114 al_play_sample(beep, 1.0, 0.0, 1.0, ALLEGRO_PLAYMODE_ONCE, 0); 115 116 117 } 118 al_destroy_display(display); 119 return 0; 120}

Edit:

This is from the FreeType documentation

Quote:

The FreeType engine doesn't cache anything, be it outlines or
bitmaps. Hence, a program that renders text by calling the glyph
loader on each letter is slow.

Because caching cannot be performed in both an easy and
portable way it is left to the application. Moreover, some
graphics systems already provide some sort of caching, and it's
better to take advantage of it rather than re-implementing it.

The hows and when of caching are explained in the "glyphs"
documentation file. The "bitmaps" doc file is also a good source
of information if you intend to render individual glyph bitmaps.

So Allegro is calling the loader each time it renders a letter? which is that loader? I'm going to need to learn the freeType caching sub-system to cache my letters? :'( .And if I do so, can I apply the changes in my games or I'm going to have to hack Allegro?.

gnolam
Member #2,030
March 2002
avatar

So Allegro is calling the loader each time it renders a letter?

Todd Cope said:

When using TrueType fonts, glyphs are cached when they are drawn for the first time.

Ah, so it just caches those characters you're using when you first draw them.





You could just draw each letter you're going to use once before starting, you know. :P

--
Move to the Democratic People's Republic of Vivendi Universal (formerly known as Sweden) - officially democracy- and privacy-free since 2008-06-18!

Thomas Fjellstrom
Member #476
June 2000
avatar

So Allegro is calling the loader each time it renders a letter?

No. As was stated, the first time you draw a letter, it is cached by allegro into a texture reserved for that font. Drawing the text initially is slow, because drawing text is slow.

Quote:

I'm going to need to learn the freeType caching sub-system to cache my letters?

As was stated in that text you quoted, no. FreeType does NO cacheing of its own. It is up to the api you're using to provide cacheing. Which allegro does. Which is why its not (horribly) slow after you draw some characters. You could just draw some text using the entire alphabet on loading, and then all the characters you use will be cached.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"Goto is the buldozer of coding. Sometimes, the buldozer is just the right tool for the job. Not often, but sometimes." -- LordBob

Elias
Member #358
May 2000

Even faster would be caching whole words or the whole text, when possible.

--
"Either help out or stop whining" - Evert

AMCerasoli
Member #11,955
May 2010
avatar

gnolam said:

You could just draw each letter you're going to use once before starting, you know.

I think I can't do something like this in my game, would be the same, but even if I could this sounds very archaic to me.

I'm loading the font in another thread, I tried to draw the font on the second thread outside the boundaries of the game but the program crashed, I know why.

But even if I draw the text as you said, that would stuck the game the same, just that isn't going to show anything... I don't want to load and "draw each letter" at the start of the game, and then send the ALLEGRO_FONT pointer to all my objects and stuff.

I want different objects loading different font and draw them without problem.

No. As was stated, the first time you draw a letter, it is cached by allegro into a texture reserved for that font. Drawing the text initially is slow, because drawing text is slow.

Drawing text isn't slow, caching them it's. So we should create a function to cache some letter before drawing them.

Quote:

As was stated in that text you quoted, no. FreeType does NO cacheing of its own. It is up to the api you're using to provide cacheing.

For that reason (FreeType does NO cacheing of its own) I was saying if I was going to need to make the caching process, if FreeType were able to make the cache of some letters, I bet there would exist a function like Todd Cope mentioned.

Quote:

Which allegro does. Which is why its not (horribly) slow after you draw some characters. You could just draw some text using the entire alphabet on loading, and then all the characters you use will be cached.

Yes but it's doing it wrong. Exaggerating it's like loading the bitmap before you want to draw it. After drawing it for first time it's very fast. And again drawing the entire alphabet besides being very archaic, isn't an option that I could take, because when I load the font my game is already running, and there are images in motion, and you can see the "cache and drawing" process that allegro make the first time when you draw the font, and see all the animations getting stuck, it takes less time to download a small file from a server than drawing text for the first time.

Elias said:

Even faster would be caching whole words or the whole text, when possible.

And this is the straw that broke the camel's back.

gnolam
Member #2,030
March 2002
avatar

But even if I draw the text as you said, that would stuck the game the same, just that isn't going to show anything...

Quote:

Drawing text isn't slow, caching them it's.

>_<
At this point, you should re-read the thread from the beginning, and then think hard about the implications of what everyone has been saying. Because you're being mind-bogglingly obtuse here.

Quote:

Yes but it's doing it wrong. Exaggerating it's like loading the bitmap before you want to draw it.

... like I said.
But maybe you're onto something. Let's skip that slow loading part and just start drawing our graphics without loading them first!

--
Move to the Democratic People's Republic of Vivendi Universal (formerly known as Sweden) - officially democracy- and privacy-free since 2008-06-18!

AMCerasoli
Member #11,955
May 2010
avatar

I don't understand... :'(

How should I solve this?? drawing some text using the entire alphabet on loading? that implies using al_draw_text() after loading right? or there is another way?

I really would prefer the function that Todd Cope mentioned...

gnolam
Member #2,030
March 2002
avatar

The function that Todd Cope mentioned would be a convenience, nothing else. Loading and rendering a glyph from the font takes time. Whether you precache x glyphs or cache x glyphs on the fly, the total time to cache x glyphs is the same.

Your problem is loading a heavy resource at runtime and expecting no impact on performance.

--
Move to the Democratic People's Republic of Vivendi Universal (formerly known as Sweden) - officially democracy- and privacy-free since 2008-06-18!

Elias
Member #358
May 2000

And this is the straw that broke the camel's back.

It's how it always worked in SDL_ttf. The function to draw text simply returns an SDL_Surface (same as ALLEGRO_BITMAP) which you then have to draw separately. And for many things it makes the most sense - for example if you display a page of text which does not change it may be like 100 times faster drawing the text to a bitmap once then just drawing that bitmap 60 times a second than drawing 1000ds of single glyphs in the page 60 times a second. Caching whole words or lines would be in-between those two.

--
"Either help out or stop whining" - Evert

AMCerasoli
Member #11,955
May 2010
avatar

Wait, I'm loading the font in another thread, using al_load_font() right?

Then when I want to draw the font, Allegro takes the characters that I want to draw and render them on runtime.

From where Allegro takes the characters ? I have no idea, I have being reading about faces and stuff like that about the FreeType library, what I know is when I want to draw my text allegro is doing two things, when in fact should just do one thing, draw the text.

So far I think there are 3 process with fonts.

  1. Loading the font from a File - slow

  2. Caching the font that I want to draw - slow

  3. Draw the font - fast

We're doing 3 processes with only two functions. If process Nº1 and Nº2 were done at the same time when calling al_load_font there wouldn't be any problem. But that is not possible, because:

Each face contains hundreds, if not thousands of distinct glyph images. Since typical text only uses a fraction of a face's glyphs, loading all glyphs at once is nearly never a good idea. Instead, it's better to only retrieve and store the ones we need more often.

I don't know if that has something to do whit the topic, but I think so.

So as you said "Whether you precache x glyphs or cache x glyphs on the fly, the total time to cache x glyphs is the same." The different with precache x glyphs from cache x glyphs on the fly is that I can do it on another thread, whitout interrupting the game.

Basically precache x glyphs would be separated form the drawing process so you can process that step on another thread.

The suggestion that people is giving my now is: "draw first the text and then is going to be fast", that's ok, but I cannot draw text on a second thread, and my game isn't based on "Loading" screens. Everything is happening on runtime, so if something haven't been loaded simply isn't shown.

The bad thing with fonts, is that even if you have loaded the font from a file isn't completely "prepared" as normal bitmaps, sounds etc... The only way would be placing a loading screen, load bitmas and other things, load the font, draw some letters and the move on. But my current implementation doesn't allow me to do that.

Edit: Reading Elias's post

Elias
Member #358
May 2000

You can't load a font in another thread. If you manage to not have it crash then that likely explains the slowness at least. You have to load it from the same thread where you call al_create_display.

(There is now a way in 5.1 svn to load the font in another thread though.)

--
"Either help out or stop whining" - Evert

AMCerasoli
Member #11,955
May 2010
avatar

I'm loading the font in another thread without any problem... I wish that was the reason for this slowness when drawing fonts, but in the example above I'm not creating another thread and the problem is exactly the same (slow as hell). I'm going to try to learn a little bit about FreeType, to see if I can find a workaround for this problem, I haven't seen the Allegro code not even once, so this is going to be really difficult to me.

No wait, I'm going to check out first the SDL and SFML libraries, try to run some font examples, if I see that is the same than Allegro, then there shouldn't be any solution... That is easier.

Elias
Member #358
May 2000

How much FPS does ex_ttf report? And how slow exactly is font drawing in your example and how do you measure it?

--
"Either help out or stop whining" - Evert

Thomas Fjellstrom
Member #476
June 2000
avatar

I'm loading the font in another thread without any problem...

Except it's likely creating the font's cache as a Memory bitmap instead of a display bitmap, which will make it slower than necessary.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"Goto is the buldozer of coding. Sometimes, the buldozer is just the right tool for the job. Not often, but sometimes." -- LordBob

kazzmir
Member #1,786
December 2001
avatar

Elias, why don't you mention the al_convert_bitmap and stuff you just made since your example uses it for exactly this purpose (converting fonts that were made in memory).

Trent Gamblin
Member #261
April 2000
avatar

How long do we have to wait before we release 5.2 ???.

Matthew Leverton
Supreme Loser
January 1999
avatar

How long do we have to wait before we release 5.2 .

Eight years.

Things that don't break backward compatibility can be candidates for 5.0 features. Just because something is in 5.1 doesn't mean it won't make it into the next 5.0 release.

Generally any incremental improvements (such as converting memory bitmaps to video bitmaps) end up in the next 5.0 release.

AMCerasoli
Member #11,955
May 2010
avatar

Ok I have already prove (to myself) that SFML it's faster than Allegro when drawing fonts, I decided to start with SFML because Elias said that SDL does the same than Allegro. I'm still working on the example to prove it, because here are many "nonbelievers" about what I'm saying...

However I found a very similar function about what Todd Coped was talking about:

bool sf::Font::LoadFromFile     (std::size_t    SizeInBytes,
                                 unsigned int   CharSize = 30,
                                 const Unicode::Text &Charset = ourDefaultCharset   
                                )

Example: MyFont.LoadFromFile("arial.ttf", 50, L"abcdefghij")


Parameters:

Filename 	: Font file to load
CharSize 	: Size of characters in bitmap - the bigger, the higher quality (30 by default)
Charset 	: Characters set to generate (by default, contains the ISO-8859-1 printable characters)

Quote:

Passing your own character set may be needed if you want to use non-ASCII characters; in this case, just pass the list of characters that you're going to use as a wide string. Unicode is of course supported, so you could as well use chinese or arabic characters. By default, the character set is defined as the extended ASCII range 31-255, (0-30 are non-printable characters) which will be enough for a default usage.

Maybe this has nothing to do, but I think it does. Seems like SFML pre-cache the extended ASCII range of letters by default when loading the font. I'll keep reading...

Elias
Member #358
May 2000

Generally any incremental improvements (such as converting memory bitmaps to video bitmaps) end up in the next 5.0 release.

So we can put things like the shaders addon into a 5.0 release? I somehow assumed we don't want any new functions in there. But thinking about it, there isn't much reason for that. As long as a game doesn't access internals, they could just replace the allegro5.dll with the newer one without re-compiling and it would keep working even if there are now additional functions.

[edit:]
AMCerasoli: did you see my two questions above?

--
"Either help out or stop whining" - Evert

Edgar Reynaldo
Member #8,592
May 2007
avatar

I have to agree with AMCerasoli, that there should be a mechanism during or after font loading that allows you to precache a range of glyphs. At least the ascii range needs to be cached for most all applications that use text.

It would be a sad day if you guys made AMCerasoli convert to SDL. :'(

 1   2   3   4 


Go to: