Slowness when drawing fonts
AMCerasoli

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

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

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

Todd Cope

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

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

AMCerasoli

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

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

Thomas Fjellstrom

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.

Elias

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

AMCerasoli
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

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!

AMCerasoli

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

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.

Elias

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.

AMCerasoli

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

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.)

AMCerasoli

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

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

Thomas Fjellstrom

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.

kazzmir

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

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

Matthew Leverton

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

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

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?

Edgar Reynaldo

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. :'(

Evert

I don't think I fully understand the problem.
The first time a glyph is used, it's cached for future use. That means that the first time you draw text, it's slower. On subsequent redraws, it should be (much) faster because the text is precached. Am I understanding this right? If so, I don't really understand why that's an issue. I mean, pre-caching wouldn't eally be faster, you'd just have to wait somewhere else for the cache to be built.

Elias said:

Caching whole words or lines would be in-between those two.

Perhaps Allegro could keep track of the most recently drawn 100 (say) strings (or words) in a cache in addition to caching glyphs?

Elias

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.

al_get_text_width(font, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890");

^ We already have it.

Evert said:

Perhaps Allegro could keep track of the most recently drawn 100 (say) strings (or words) in a cache in addition to caching glyphs?

Yes, but would have to test my assumption about it first. I wonder how e.g. Firefox or Chrome draw all their text - but I'd think they also use per-glyph caching. At least in Firefox text is 100% identical to Allegro 5... i.e. when I use the same font with the same size then all glyphs are at the exact same pixel positions (each separate glyph position rounded to a whole integer pixel position just like in A5).

What might be the real problem is how long one al_draw_bitmap() call takes. A specialized text drawer which uses al_hold_bitmap_drawing or even al_draw_prim is a lot faster (using the very same texture cache).

Also, the ttf addon isn't very clever about texture management right now. We should probably at least have an al_optimize_ttf_cache(ALLEGRO_FONT *) function which tries to combine all cached-so-far glyphs into a single texture.

In any case, I still have no idea how slow or fast font drawing is... if only the caching is a problem then my code snippet above fully solves the issue :)

AMCerasoli
Evert said:

The first time a glyph is used, it's cached for future use. That means that the first time you draw text, it's slower. On subsequent redraws, it should be (much) faster because the text is precached. Am I understanding this right?

More or less. Isn't that I completely understand the process, but as you have said "The first time a glyph is used, it's cached for future use" this is done by Allegro and not by FreeType, basically is a bad implementation when drawing fonts, because caching the glyphs immediately before draw them makes the program take the glyphs from a truetype font file,"prepare" them from a bunch of vectors depending on the size, all this on runtime.

Quote:

I don't really understand why that's an issue. I mean, pre-caching wouldn't really be faster, you'd just have to wait somewhere else for the cache to be built.

We're talking about pre-caching because Allegro isn't caching the glyphs before they are drawn at runtime. But actually we should be talking about caching the glyphs, just that, everyone knows that this should be done before we draw the text to the screen, and not at runtime.

Separating the caching from the drawing, has 2 very important advantages:

  1. It lets you create multi-thread applications, where you can load the font on a different thread and in which you cannot actually draw anything so you can't cache the glyphs. Therefore you rely on the main thread, which should be the UI thread, so when you draw the font you would be caching them too, stopping the animations or other stuffs (My case)

  2. You don't need to draw the alphabet outside of the screen to cache the fonts in a loading phase

Quote:

glyph caching
Each time you request a glyph image from a font, FT2 does it by parsing the relevant portion of the font file/stream and interpreting it according to its font format. This can be very slow for certain formats, including scalable ones like TrueType or Type 1.

Any decent text-rendering sub-system must thus be capable of caching glyph data in order to reach appropriate rendering speed

Note that we provide a beta caching sub-system with FreeType 2 since version 2.0.1. Even though you're free to write your own to suit your needs.

So I think the best solution is that one adopted by SFML where they have the extended ASCII range already cached on loading, and if you want you can select another fonts if you want. something like al_load_font(char const *filename, int size, char const*charset, int flags). That function should be caching the extended ASCII range of character, because it's illogical to be writing "abcdefg..." each time we load a font, or we could create another function to do the cache of estrange characters. Even the Spanish alphabet fits very well in the extended ASCII range, so would be a good idia having a separated function... It's the same though.

Edit: Again... reading Elias post... Just because my English isn't that well...

Edit2: That function actually cache those letters?... that might be something. I'll try it. :D

Edit3:
Aww... That it's worse :( . That reduce my FPS not only when I draw the font for first time... But also still slow for the rest of the game...

Maybe because I'm doing it on another thread:

     al_lock_mutex(p_dato->mutex);
     p_dato->font = al_load_font("font/consola.ttf",17, 0);
     al_get_text_width(p_dato->font, "abcdefghijklmnopqrstuvw...");
     al_unlock_mutex(p_dato->mutex);

I'm in checkmate... :-/

Slartibartfast

It lets you create multi-thread applications, where you can load the font on a different thread and in which you cannot actually draw anything so you can't cache the glyphs.

Not it does not because it is exactly as you say, if you load the font on a different thread you can't cache the glyphs, this is because the other thread does not have the proper context to create a video bitmap. (To effectively cache the font you draw the glyphs into a video bitmap and further use of those glyphs comes from that video bitmap.)
The best you can do from another thread is cache the font into a memory bitmap (not a video bitmap), which will give much worse performance when using that font. (video bitmaps can make use hardware accelerated drawing, memory bitmaps cannot)

Note that everything I just wrote I learned solely by reading this thread, as I didn't know about these things beforehand and it has all been explained to you in this thread numerous times.

EDIT:

Quote:

Maybe because I'm doing it on another thread:

Maybe because the glyphs are being cached to a memory bitmap? Because they are cached in a thread that can only create memory bitmaps?

Try caching the font from the "main" thread.

Evert

"The first time a glyph is used, it's cached for future use" this is done by Allegro and not by FreeType, basically is a bad implementation when drawing fonts, because caching the glyphs immediately before draw them makes the program take the glyphs from a truetype font file,"prepare" them from a bunch of vectors depending on the size, all this on runtime.

I still don't understand.
This work has to be done at least once, either when the font is loaded (but you can't do it for all glyphs in the font, there's potentially far too many of those) or on-demand when it turns out that those characters are actually needed.
The question is, do you do more work up front (at the risk of doing work that you don't need to do), or do you do work when it turns out that you have to? In general, especially when it comes to data processing, you're better off doing the latter.
Yes, it means that the first time you request the data (want to draw the glyph), it's slower because you first have to do extra work. But that overhead should be gone the next time you use the data, right? So what's the problem?

Is it really a problem if you get a small slow-down the first time you use a bit of data as long as it's gone when you re-use the data afterwards? Your runtime performance is not going to be determined by how fast things are the first time you draw them, but by how fast things are when you redraw them every frame.

EDIT

the other thread does not have the proper context to create a video bitmap.

Quoted for emphasis.

AMCerasoli
Elias said:

At least in Firefox text is 100% identical to Allegro 5... i.e. when I use the same font with the same size then all glyphs are at the exact same pixel positions (each separate glyph position rounded to a whole integer pixel position just like in A5).

Yes the same in PhotoShop, it's very precise.

Not it does not because it is exactly as you say, if you load the font on a different thread you can't cache the glyphs,

Have you run my example? even on a multi core computer I can notice the application getting stuck, there, I'm not using Multi-thread SO THAT ISN'T THE REASON.

Quote:

Note that everything I just wrote I learned solely by reading this thread, as I didn't know about these things beforehand and it has all been explained to you in this thread numerous times.

Nice!, I'm proud of you

Quote:

Try caching the font from the "main" thread.

That is what I did in my example, check it.
Edit:

Evert said:

This work has to be done at least once, either when the font is loaded (but you can't do it for all glyphs in the font, there's potentially far too many of those)

You can't do it for all Unicode character, TTF files not even has so many charter inside anyway. But you could do it for your alphabet.

What you're suggesting is that if for some reason I want to show text on my game in two idioms at the same time it's more logic to block out the entire UI while the process is caching the glyphs for first time when drawing, than doing that previously, even better in another thread?

You know what?, fine, there is no problem, everything is ok, I'll take this into account the next time I'm on a project, or the next time I find a bug or whatever.

Slartibartfast

That is what I did in my example, check it.

Actually, it isn't. The relevant glyphs are cached when first used, not when loaded.

Quote:

Have you run my example?

Yes. It runs blindingly fast. There is a tiny, insignificant slowdown when first writing with the font, and that insignificant slowdown disappears if I manually cache the font. (In this case by replicating the call to al_draw_text before entering the main loop)

Quote:

Have you run my example?

Did you read your own message that I was responding to? Allow me to quote it for you:

Separating the caching from the drawing, has 2 very important advantages:
1) It lets you create multi-thread applications, where you can load the font on a different thread and in which you cannot actually draw anything so you can't cache the glyphs. Therefore you rely on the main thread, which should be the UI thread, so when you draw the font you would be caching them too, stopping the animations or other stuffs (My case)

You claimed that separating caching from drawing will allow you to load the font from a different thread, and I explained to you that either way you cannot load the font from a different thread.

Matthew Leverton

AMCerasoli, if you use a smaller font does it work? Maybe Allegro is creating a memory bitmap cache. (I don't know how it works...) Your example works fine for me.

Elias said:

So we can put things like the shaders addon into a 5.0 release?

Theoretically, yes. While it may seem more proper to put big new features into a 5.2 branch, it (in my opinion) isn't worth it because maintaining two major releases is a pain in the butt. People will expect the older 5.0 branch to be continued to be maintained (bug fixed), which is just a hassle when it comes to providing binaries.

I would leave 5.2 alone until we come up with things that either completely break backward compatibility or are major changes to the core or how things are done. I don't know what would fit in that latter category.

Peter W's opinion would be more useful than mine.

Thomas Fjellstrom

People will expect the older 5.0 branch to be continued to be maintained (bug fixed), which is just a hassle when it comes to providing binaries

I'm not sure why, it was never done for 3.x or 4.x. As soon as a new .x release is made, the last one is abandoned afaik.

I'm pretty sure 3.x was abandoned entirely when 4.x was released. So maintaining 4.x and 5.x right now is a bit of an aberration.

Elias

Indeed, when 5.2 gets out 5.0 is deprecated. Just like you had to upgrade from 4.0 to 4.2 if you still wanted to benefit from bug fixes. There also was no more 4.2.x releases after 4.4.0 got released (I think at least, didn't follow 4.x development so closely).

Matthew Leverton

If 5.0 is abandoned the instant 5.2 comes out, then switching to 5.2 for any reason other than breaking backward compatibility is purely psychological. And since we aren't in a war with SDL (etc) to see who has the largest version number, then I'd stick with 5.0 as long as we aren't breaking things.

AMCerasoli

AMCerasoli, if you use a smaller font does it work? Maybe Allegro is creating a memory bitmap cache. (I don't know how it works...) Your example works fine for me.

My font is already very small (17):

{"name":"604230","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/9\/59e3b6697ce14975f343cf8476a526f7.png","w":805,"h":633,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/9\/59e3b6697ce14975f343cf8476a526f7"}604230

Maybe you have a 360 teraflops computer, but I have tasted the game on different computers and the fastest one still being slow, I'm doing a simple game I can't demand too much in the minimum requirements.

Elias said:

In any case, I still have no idea how slow or fast font drawing is... if only the caching is a problem then my code snippet above fully solves the issue

ehh... No it doesn't... I have test it using my example and the text still slow when it's drawn for first time.

I have modified the ex_ttf file to show you the problem, if you want I can send you the file, but I think the problem is very clear right now.

video

You can see the lot of FPS that are drooped the first time the text is loaded and that is just a small amount of text with an small size. I'm not a nit-picky guy, guys, but it's drooping my game frames very noticeable... I hope in future version of Allegro the glyph are cached on loading like SFML does.

Elias

ehh... No it doesn't... I have test it using my example and the text still slow when it's drawn for first time.

Are you sure that you:

- Included all glyphs in the cache?
- Do not do the caching from another thread/without a display?

Evert

What you're suggesting is that if for some reason I want to show text on my game in two idioms at the same time

"two idioms at the same time"???

Quote:

it's more logic to block out the entire UI while the process is caching the glyphs for first time when drawing, than doing that previously, even better in another thread?

No, I'm saying that if that's the issue that's being discussed, I don't really see why that's a problem. And the reason I was asking is because it wasn't entirely clear to me what the issue was/is.
And why bring up "another thread" if you find it necessary to reply to someone else in bold face that you're not using multiple threads?

Quote:

You know what?, fine, there is no problem, everything is ok, I'll take this into account the next time I'm on a project, or the next time I find a bug or whatever.

Count to ten first next time you feel the urge to post.

You can see the lot of FPS that are drooped the first time the text is loaded and that is just a small amount of text with an small size.

You're aware of course that your monitor is only able to display ~60fps, right?
Anyway, are you serious? You get a momentary blip going from ~580fps to ~520fps the first time the text is drawn. Is that really a big deal and what you call a "massive slowdown"?
I'm not saying there can't be an issue or things can't be improved, but if this is quantitatively what we're talking about, I really don't see where you're coming from. Sorry.

AMCerasoli
Evert said:

"two idioms at the same time"???

Yes Evert, two idioms at the same time... Draw two languages at the same time... You looks like a compiler...

To do something like that, I would have to 1) load a .ttf which has the letters for the two different idioms (Ex: German, Italian), or load two different .tff one for German and another for Italian... Anyway my point was that the more letter you want to draw the slower the process is going to be. Without a cache function if for some reason you want to do something like this:

Hello Evert
مرحبا ايفرت
прывітанне Эвэрт
喜埃弗特
היי Evert

Your game UI is going to get block for three weeks with the current implementation, at least with a cache function I could put a "Loading..." on the window and don't block the UI, while I'm processing the cache process on another thread, yes I know, "I cannot" load fonts on another thread. I'm going to reimplement my game structure to adjust to this conditions, I didn't know that loading things on another thread would be loaded as memory bitmaps, because I guess it's the same with normal bitmaps. For that reason a al_convert_bitmap function would be good. But I think I can fix the necessity to load the font on another thread with some changes, I can't fix the Super Extra Disproportionate time that it take to draw them for first time, though.

Quote:

No, I'm saying that if that's the issue that's being discussed, I don't really see why that's a problem. And the reason I was asking is because it wasn't entirely clear to me what the issue was/is.

Sorry... I didn't get that way... I use to simply say: "ohhh ok".

Quote:

And why bring up "another thread" if you find it necessary to reply to someone else in bold face that you're not using multiple threads?

I'm bringing up "another thread" because I want to make sure developers know what I'm doing in case I'm doing something wrong, which I'm actually doing (loading fonts in another thread) supposedly should fail, but it doesn't (so far).

If that "someone" had read the entire thread he would know that I'm using multi-thread but the example (which is presenting the same problem) isn't.

Quote:

You're aware of course that your monitor is only able to display ~60fps, right?

::)

Quote:

Anyway, are you serious? You get a momentary blip going from ~580fps to ~520fps the first time the text is drawn. Is that really a big deal and what you call a "massive slowdown"?
I'm not saying there can't be an issue or things can't be improved, but if this is quantitatively what we're talking about, I really don't see where you're coming from. Sorry.

I'm not a conformist, if I see that my application is getting stuck for 0.00001 seconds I want to know why, if isn't my fault, I want to know what is doing the library, and if I found there is a problem with the library, I will report it. But I just want to clarify that the stuck time isn't 0.00001 seconds it's much more, and this is an issue.

Edit:

Elias said:

- Included all glyphs in the cache?
- Do not do the caching from another thread/without a display

Yes and yes.

#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 al_get_text_width(font, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); 82 al_get_text_width(font2, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); 83 al_get_text_width(font3, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); 84 al_get_text_width(font4, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); 85 al_get_text_width(font5, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); 86 87 while(1){ 88 al_get_keyboard_state(&keyboard); 89 90 al_clear_to_color(al_map_rgb(50,10,70)); 91 92 if(al_key_down(&keyboard, ALLEGRO_KEY_A)) 93 al_draw_text(font, al_map_rgba(255,255,255,255), 640/2, (480/4),ALLEGRO_ALIGN_CENTRE, "Slow Font : (!"); 94 95 else if(al_key_down(&keyboard, ALLEGRO_KEY_S)) 96 al_draw_text(font2, al_map_rgba(255,255,255,255), 640/2, 50+(480/4),ALLEGRO_ALIGN_CENTRE, "Slow Font : (!"); 97 98 else if(al_key_down(&keyboard, ALLEGRO_KEY_D)) 99 al_draw_text(font3, al_map_rgba(255,255,255,255), 640/2, 100+(480/4),ALLEGRO_ALIGN_CENTRE, "Slow Font : (!"); 100 101 else if(al_key_down(&keyboard, ALLEGRO_KEY_F)) 102 al_draw_text(font4, al_map_rgba(255,255,255,255), 640/2, 150+(480/4),ALLEGRO_ALIGN_CENTRE, "Slow Font : (!"); 103 104 else if(al_key_down(&keyboard, ALLEGRO_KEY_G)) 105 al_draw_text(font5, al_map_rgba(255,255,255,255), 640/2, 200+(480/4),ALLEGRO_ALIGN_CENTRE, "Slow Font : (!"); 106 107 else if(al_key_down(&keyboard, ALLEGRO_KEY_B)) 108 for(int a=0; a<10;a++) 109 al_draw_bitmap(Obj_Bmp2, 200+rand()%40, 200+rand()%40, 0); // This Bitmap haven't being drawn previusly. 110 111 else if(al_key_down(&keyboard, ALLEGRO_KEY_ESCAPE)) 112 break; 113 114 for(std::list<SPOT*>::iterator it = Spo_List.begin(); it != Spo_List.end(); it++) 115 (*it)->move(); 116 117 al_flip_display(); 118 119 al_rest(0.01); 120 //Beep(500,20); 121 al_play_sample(beep, 1.0, 0.0, 1.0, ALLEGRO_PLAYMODE_ONCE, 0); 122 123 124 } 125 al_destroy_display(display); 126 return 0; 127}

Trent Gamblin

I hope in future version of Allegro the glyph are cached on loading like SFML does.

That's not going to happen. The fonts (2 of them) I'm using for my current game have over 30,000 characters each. If you need glyphs cached just draw them like a bunch of people have said. Caching glyphs is slow, there's no way around it, but dropping to 520 FPS is not even an issue.

Evert

Yes Evert, two idioms at the same time... Draw two languages at the same time... You looks like a compiler...

Look, I appreciate that your English is not very good and that we're all going to make an effort to understand what you mean, but "two idioms at the same time" just doesn't make sense as a sentence. It certainly doesn't mean "display two languages that require vastly different character sets".
If it's not clear what you mean, it's up to you to make the effort of being more clear.

Quote:

For that reason a al_convert_bitmap function would be good.

I think Elias committed that to SVN the other day, have a look.

Quote:

::)

What?
It doesn't, and if you lock to the update frequency of the screen (what used to be called "wait for vsync", not sure what the best phrase to use these days is) and/or only draw to the screen when you've actually changed something so you need to redraw, you're never going to get a bogus "500 fps".

Quote:

if I see that my application is getting stuck for 0.00001 seconds I want to know why,

Hyperbole can sometimes be very effective, but if you push it too far it just becomes ridiculous.

Quote:

if isn't my fault, I want to know what is doing the library, and if I found there is a problem with the library, I will report it.

Well, you know what it's doing and why the delay is there. It's not a "problem" in the sense that the behaviour is by design.
I'm not saying it's not nice to have a way to pre-cache data you already know you're going to need, but based on the numbers you've posted, I don't see there's a major problem with the current implementation.

Quote:

But I just want to clarify that the stuck time isn't 0.00001 seconds it's much more, and this is an issue.

The drop in framerate corresponds to a processing time of 0.2ms. You want to display a "loading..." message for that?

AMCerasoli

hahaha It's funny how all this reminds me when I was in the school... Now I could say something like:

Evert said:

Look, I appreciate that your English is not very good and that we're all going to make an effort to understand what you mean, but "two idioms at the same time" just doesn't make sense as a sentence. It certainly doesn't mean "display two languages that require vastly different character sets".
If it's not clear what you mean, it's up to you to make the effort of being more clear.

a word to the wise is sufficient...

Quote:

What?
It doesn't, and if you lock to the update frequency of the screen (what used to be called "wait for vsync", not sure what the best phrase to use these days is) and/or only draw to the screen when you've actually changed something so you need to redraw, you're never going to get a bogus "500 fps".

I know man, that's just an example, my current game is running at 30FPS logic and drawing, for that reason I can see it.

Quote:

Hyperbole can sometimes be very effective, but if you push it too far it just becomes ridiculous.

It's just an example

Quote:

Well, you know what it's doing and why the delay is there. It's not a "problem" in the sense that the behaviour is by design.

Of course it is!

Quote:

I'm not saying it's not nice to have a way to pre-cache data you already know you're going to need, but based on the numbers you've posted, I don't see there's a major problem with the current implementation.

The numbers that I'm posting are from an example!, I said "I have modified the ex_ttf file to show you the problem", if the FPS drooped were worse in that simple example, then there would have a really bad bad implementation.

Quote:

The drop in framerate corresponds to a processing time of 0.2ms. You want to display a "loading..." message for that?

That was just an example... I want to cry...

That's not going to happen. The fonts (2 of them) I'm using for my current game have over 30,000 characters each. If you need glyphs cached just draw them like a bunch of people have said. Caching glyphs is slow, there's no way around it, but dropping to 520 FPS is not even an issue.

Allegro would only cache the extended ANSCII range, and there would be another function for people which uses other glyphs... the 520 FPS was an example... I think I'm going to commit suicide.

But doesn't matter, if is not going to happen, isn't going to happen, it's fine, now depend on my which decision to take. Thanks to all.

Trent Gamblin

Why cache the extended ascii range? You have no idea what glyphs the user is going to use until they draw them. That's why you draw the glyphs you need yourself, they'll be cached, and you move on.

kazzmir

AMCerasoli can you be specific about the kind of slowdowns you are seeing? Exactly how much time does it take to render the font the first time? I took your original example and added some timing measurements to it. I see this:

14.090000 milliseconds
0.169000 milliseconds
0.159000 milliseconds
0.160000 milliseconds
0.163000 milliseconds
0.179000 milliseconds

So caching takes 2 orders of magnitude longer than drawing but still 14ms is insanely small. Are you seeing something on the order of seconds?

Attached is your main.cpp but with calls to gettimeofday (I only measured pressing the A key).

Evert

I know man, that's just an example, my current game is running at 30FPS logic and drawing, for that reason I can see it.

The problem is, you start out by saying "text is drawn slowly the first time round", which is true. There's a reason for why that is the case, and the reason is that the first time a character is used, Allegro caches the glyphs so that things are faster the next time they're used.
You then proceed to say that there's a horrible drop in FPS when text is drawn for the first time, and show an example that... doesn't show a horrible drop in FPS at all.

Quote:

It's just an example

Yes. A bad one, because your hyperbole was very over the top.
This is exactly the problem: you talk about horrible slow downs, weeks of rendering time, wanting to find out why your game takes 0.00001 seconds longer to do something and yet the only actual data you present don't show any indication of a problem that even remotely justifies that level of hyperbole.

Please try to be more clear about how big of a problem we're dealing with here.

Quote:

Allegro would only cache the extended ANSCII range, and there would be another function for people which uses other glyphs...

First of all, there's no such thing as "extended ASCII range", Allegro uses UTF-8 encoding. Allegro could precache the full standard Latin alphabet, but if I only intend to use the fint to display a score, that's wasting a LOT of cache for no gain (and if we're talking about slowdowns, that'll slow down font loading for no gain as well). That's what I meant earlier: don't do work now that you don't know you need to do later.

Quote:

the 520 FPS was an example...

Again, a bad example because it doesn't actually support your claim that there's a massive problem. Just quote numbers from your game.

Quote:

But doesn't matter, if is not going to happen, isn't going to happen, it's fine, now depend on my which decision to take. Thanks to all.

What isn't going to happen is us spending a lot of time trying to avoid a drop from 580 to 520 FPS. However, a drop from 30 to (say) 5 FPS is a very different story.
So once again, please try to be more clear and precise.

Edgar Reynaldo
Elias said:

Edgar said:

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.

al_get_text_width(font, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890");

^ We already have it.

The manual says absolutely nothing about al_get_text_width caching glyphs. Are we supposed to be psychic?

I wrote up some test code. Pass the path to the font you want to test on the command line. It caches all the printable regular ascii characters and measures the time it takes to load, cache, and display them :

#SelectExpand
1 2#include <cstdio> 3 4#include "allegro5/allegro.h" 5#include "allegro5/allegro_font.h" 6#include "allegro5/allegro_ttf.h" 7 8 9class AllegroSetup { 10private : 11 ALLEGRO_DISPLAY* display; 12 ALLEGRO_TIMER* timer; 13 ALLEGRO_EVENT_QUEUE* event_queue; 14 bool ready; 15public : 16 AllegroSetup(int w , int h , double tick_time) : 17 display(0), 18 timer(0), 19 event_queue(0), 20 ready(false) 21 { 22 if (!al_init()) {printf("Failed to initialize Allegro.\n");return;} 23 if (!(display = al_create_display(w,h))) { 24 printf("Failed to create %i X %i display.\n" , w , h); 25 return; 26 } 27 if (!(timer = al_create_timer(tick_time))) { 28 printf("Failed to create %lg paced timer.\n" , tick_time); 29 return; 30 } 31 if (!al_install_keyboard()) { 32 printf("Failed to initialize keyboard.\n"); 33 return; 34 } 35 if (!al_install_mouse()) { 36 printf("Failed to install mouse.\n"); 37 return; 38 } 39 if (!(event_queue = al_create_event_queue())) { 40 printf("Failed to create event queue.\n"); 41 return; 42 } 43 al_register_event_source(event_queue , al_get_keyboard_event_source()); 44 al_register_event_source(event_queue , al_get_display_event_source(display)); 45 al_register_event_source(event_queue , al_get_mouse_event_source()); 46 al_register_event_source(event_queue , al_get_timer_event_source(timer)); 47 ready = true; 48 } 49 50 ~AllegroSetup() { 51 if (event_queue) {al_destroy_event_queue(event_queue);} 52 if (display) {al_destroy_display(display);} 53 } 54 55 bool Ready() {return ready;} 56 57 inline operator ALLEGRO_DISPLAY* () {return display;} 58 inline operator ALLEGRO_EVENT_QUEUE* () {return event_queue;} 59 inline operator ALLEGRO_TIMER* () {return timer;} 60}; 61 62 63int readkey(ALLEGRO_EVENT_QUEUE* eq) { 64 while (1) { 65 ALLEGRO_EVENT ev; 66 al_wait_for_event(eq , &ev); 67 if (ev.type == ALLEGRO_EVENT_KEY_CHAR) { 68 return ev.keyboard.keycode; 69 } 70 } 71 return 0; 72} 73 74 75 76int main(int argc , char** argv) { 77 78 const char* font_name = "verdana.ttf"; 79 80 if (argc > 1) { 81 font_name = argv[1]; 82 } 83 84 85 AllegroSetup allegro(800,600,1.0/60.0); 86 if (!allegro.Ready()) {return 1;} 87 88 al_init_font_addon(); 89 al_init_ttf_addon(); 90 91 double start = 0.0; 92 double stop = 0.0; 93 double load_time = 0.0; 94 double cache_time = 0.0; 95 double display_time = 0.0; 96 97 double total_load_time = 0.0; 98 double total_cache_time = 0.0; 99 double total_display_time = 0.0; 100 101 int count = 0; 102 103 const int NUM = 13; 104 const char* strs[NUM] = { 105 "abcde", 106 "fghij", 107 "klmno", 108 "pqrst", 109 "uvwxyz", 110 "01234", 111 "56789", 112 "!@#$%", 113 "^&*()", 114 "-=_+", 115 "[]{}\\|", 116 ",.<>/?", 117 ";:'\"" 118 }; 119 120 bool quit = false; 121 122 while (!quit) { 123 ALLEGRO_EVENT ev; 124 while (!al_is_event_queue_empty(allegro)) { 125 al_wait_for_event(allegro , &ev); 126 if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) { 127 quit = true; 128 break; 129 } 130 else if (ev.type == ALLEGRO_EVENT_KEY_DOWN) { 131 if (ev.keyboard.keycode == ALLEGRO_KEY_ESCAPE) { 132 quit = true; 133 break; 134 } 135 } 136 } 137 if (quit) { 138 if (!count) {count = 1;} 139 break; 140 } 141 142 // measure loading time 143 start = al_get_time(); 144 ALLEGRO_FONT* font = al_load_ttf_font(font_name , -20 , 0); 145 stop = al_get_time(); 146 load_time = stop - start; 147 total_load_time += load_time; 148 if (!font) { 149 printf("Font file %s failed to load!\n" , font_name); 150 if (!count) {count = 1;} 151 break; 152 } 153 154 al_clear_to_color(al_map_rgb(0,0,0)); 155 156 // measure caching time 157 start = al_get_time(); 158 for (int i = 0 ; i < NUM ; ++i) { 159 int x = al_get_text_width(font , strs[i]); 160 } 161 stop = al_get_time(); 162 cache_time = stop - start; 163 total_cache_time += cache_time; 164 165 ALLEGRO_COLOR white = al_map_rgb(255,255,255); 166 // measure drawing time 167 start = al_get_time(); 168 for (int i = 0 ; i < NUM ; ++i) { 169//void al_draw_text(const ALLEGRO_FONT *font , ALLEGRO_COLOR color, float x , float y , int flags , char const *text) 170 al_draw_text(font , white , 10 , 10 + 30*i , 0 , strs[i]); 171 } 172 stop = al_get_time(); 173 display_time = stop - start; 174 total_display_time += display_time; 175 176 ++count; 177//void al_draw_textf(const ALLEGRO_FONT *font, ALLEGRO_COLOR color , float x , float y , int flags , const char *format, ...) 178 al_draw_textf(font , white , 410 , 10 , 0 , "Average load time = %0.3f ms" , 1000.0f*((float)total_load_time / (float)count)); 179 al_draw_textf(font , white , 410 , 40 , 0 , "Average cache time = %0.3f ms" , 1000.0f*((float)total_cache_time / (float)count)); 180 al_draw_textf(font , white , 410 , 70 , 0 , "Average draw time = %0.3f ms" , 1000.0f*((float)total_display_time / (float)count)); 181 al_draw_textf(font , white , 410 , 100 , 0 , "Test count = %i tests" , count); 182 183 al_flip_display(); 184 185 al_destroy_font(font); 186 } 187 188 printf("Average load time = %0.3lf ms\n" , 1000.0*(total_load_time / (double)count)); 189 printf("Average cache time = %0.3lf ms\n" , 1000.0*(total_cache_time / (double)count)); 190 printf("Average display time = %0.3lf ms\n" , 1000.0*(total_display_time / (double)count)); 191 printf("Test count = %i\n" , count); 192 193 return 0; 194}

I ran it for about 10 minutes and here are my results :

c:\ctwoplus\progcode\allegro5\test>A5Bouncer.exe
Average load time     = 0.980 ms
Average cache time    = 305.909 ms
Average display time  = 0.368 ms
Test count = 2130

c:\ctwoplus\progcode\allegro5\test>

So there is definitely a noticeable delay when caching the glyphs - just over 3/10ths of a second.

I still think there should be an explicit function in Allegro 5 that will do this for you, and al_get_text_width is cumbersome to use when you have different languages and varied characters in use. It could be as simple as :

void al_cache_glyph_range(ALLEGRO_FONT* font , int start , int number);

Imagine the case where users wish to display Kanji - they will almost always be caching new glyphs, and there will be noticeable slowdowns during their program every time they draw new text.

PS. Why does verdana.ttf look like shit (some characters are noticeably lighter or darker than others) ?
{"name":"604233","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/1\/a1a1d12e510134d770d50f9f39bc9134.png","w":812,"h":632,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/1\/a1a1d12e510134d770d50f9f39bc9134"}604233

Arthur Kalliokoski

PS. Why does verdana.ttf look like (some characters are noticeably lighter or darker than others) ?

It seems to me that the anti aliasing isn't allowing any pixels in the thinnest parts to be drawn full white. I've tried forcing pixels to be all white past a certain threshold, but then they have the jaggies, looking even worse. If you want a small font to look good, choose a font that cooperates.

Thomas Fjellstrom

The manual says absolutely nothing about al_get_text_width caching glyphs. Are we supposed to be psychic?

No, but it has to render the characters to be able to measure the length a given string will be when drawn. I was kinda surprised at that too the first time I heard about it.

Edgar Reynaldo

It seems to me that the anti aliasing isn't allowing any pixels in the thinnest parts to be drawn full white. I've tried forcing pixels to be all white past a certain threshold, but then they have the jaggies, looking even worse. If you want a small font to look good, choose a font that cooperates.

You're right, it was just a shitty font. I tried using AMCerasoli's consola.ttf font, and it looks a 1000 times better with the same dimensions.

No, but it has to render the characters to be able to measure the length a given string will be when drawn. I was kinda surprised at that too the first time I heard about it.

That doesn't make much sense - what do you do, test pixel colors? Can't you calculate the width given the information in the font? Why does it have to be drawn first?

Thomas Fjellstrom

Why does it have to be drawn first?

I don't know. But I assume it has to do with how non fixed width fonts will render a specific sentence. A sentence's length depends on the order of the characters. Sometimes a row here or there is skipped.

Edgar Reynaldo

@Arthur
I got verdana.ttf to look good by using the flag ALLEGRO_TTF_MONOCHROME when loading the font. It disables anti-aliasing for the font. It also made consola.ttf look chunky and ugly though, so it doesn't work for every font.

@Thomas
It still seems like it should just be a sum of widths and offsets based on a lookup. Without knowing the guts of Allegro 5 and FreeType though, I couldn't say if it could be done.

I expanded my test to include a second measurement of al_get_text_width for the same strings, and it took just as long as the first time! That has got to be the slowest function I've seen in a while. 13 5 character strings takes 0.3 seconds to measure the width of? I guess that precludes me from making any text editors with Allegro 5 then.

Why doesn't al_get_text_width take advantage of the fact that the glyphs it is using are already cached?

Peter Wang

Theoretically, yes. While it may seem more proper to put big new features into a 5.2 branch, it (in my opinion) isn't worth it because maintaining two major releases is a pain in the butt. People will expect the older 5.0 branch to be continued to be maintained (bug fixed), which is just a hassle when it comes to providing binaries.

I would leave 5.2 alone until we come up with things that either completely break backward compatibility or are major changes to the core or how things are done. I don't know what would fit in that latter category.

Peter W's opinion would be more useful than mine.

I've had problems with my internet connection the last few days, so I haven't been following closely.

My plan was to stick with the Allegro 4 model, of which there are two. In 4.0.x we maintained forward and backwards compatibility, meaning no new symbols would be added in a stable branch. In 4.2.x, we dropped the forwards compatibility requirement. Following that model, we could add new symbols within the 5.0.x branch. I haven't done that so far, even for small additions.

Our development model doesn't really allow backporting of big changes. All sorts of stuff gets dumped together into the unstable branch then refined over time. It's not easy to untangle the final state of a single feature for inclusion into the stable branch. The alternative is to stabilise all the new features together at once, then declare the unstable branch 'stable'. Since we already call the unstable branch '5.1', it would be a bit confusing to release it as '5.0.4'... but possible.

Matthew Leverton

Whether something is called 5.0.X or 5.2.0 doesn't matter to me as long as we aren't trying to maintain 5.0 for very long after 5.2 is released. Obviously programs that dynamically link against 5.0 would cease getting free upgrades, but I don't particularly care about that...

Would you include new addons in 5.0? Seems like those shouldn't require any changes to the core Allegro that would be hard to backport.

Slartibartfast

If that "someone" had read the entire thread he would know that I'm using multi-thread but the example (which is presenting the same problem) isn't.

Someone has read the entire thread before trying to help you.
Someone has also seen it explained to you over and over again that:
1) You must load fonts from the "main" thread. (technically you have to load them from the thread that creates the display if I understand correctly)
2) If certain glyphs in the font are uncached, they will be cached when they are first drawn (because there's no better way to automatically choose what to cache) but
3) You can cause the glyphs to be cached yourself (for example by writing "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456780-=" once before that actual game logic is started), which means that there will be no delays during the game when first writing something.
This is it. This is how things are.

You however keep repeating that allegro should cache some glyphs for you when first loading the font, and again it has been explained to you that it will only move the "horrible delay" from when you (the programmer) choose to draw/cache the font (for example, by doing (3) from before) to the loading function.
You can even implement the function you want to have by yourself!
Something like: (not guaranteed to work, just an example)

ALLEGRO_FONT *AMCerasoli_load_and_cache(const char *filename, int size, int flags)
{
 ALLEGRO_FONT *font = al_load_font(filename, size, flags);
 al_get_text_width(font, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890");
}
ALLEGRO_FONT *AMCerasoli_load_and_cache(const char *filename, int size, int flags)
{
 ALLEGRO_FONT *font = al_load_font(filename, size, flags);
 al_get_text_width(font, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890");
}

my current game is running at 30FPS logic and drawing, for that reason I can see it.

I probably shouldn't ask, but how is your game timed to 30FPS? You didn't by chance time it like the example by simply calling al_rest() after each loop, right?

Elias

The manual says absolutely nothing about al_get_text_width caching glyphs. Are we supposed to be psychic?

No, the manual and/or code should be improved. That's why threads like this are good (if anyone will care to submit a patch) :)

Quote:

PS. Why does verdana.ttf look like (some characters are noticeably lighter or darker than others) ?

Try setting your web browser to use verdana.ttf (with the same size). Does it look different there? If not then the problem is with the font. Otherwise if it looks better than Allegro there, try using another program using freetype (for me that's firefox on linux, which has pixel-by-pixel 100% identical rgb values to Allegro here). If there is a difference then the ttf addon does something wrong and we should try to fix it.

I expanded my test to include a second measurement of al_get_text_width for the same strings, and it took just as long as the first time!

Hm, maybe al_get_text_width doesn't cache after all, let me check the code...

[edit:]

I can't try any examples from here, but at least it does look like it uses the cache (if (glyph->bitmap)): http://allefant.com/gtags/S/1921.html#L178

Is your example updated to do that second measurement? kazzmir's timing results suggest that caching is being used (and improves time a lot). But AMCerasoli also saw no improvement after the al_get_text_width() line it seems... does allegro.log say anything suspicious? There may very well be some bug in the glyph caching code (I remember reports here where the first cached glyph was never drawn for example but could never find a cause.)

Maybe you and AMCerasoli are both using DirectX but everyone else is using OpenGL? And then the bug would be somewhere in the DirectX code, maybe it fails locking very small bitmap regions very often or something.

Michał Cichoń

Linux make worst possible use of FreeType, so this is bad example. The cause of odd looking Vertana is probably in A5 TTF addon code. I didn't looked at the it yet.

Elias

Linux make worst possible use of FreeType

Got some reference for that?

Michał Cichoń

I think this describe the issue.
Mostly I agree with opinion in this article.

Elias

Interesting, but at least in my Linux (Ubuntu, but manually changed the gnome font settings until it looked good) Freetype just seems to be perfectly tuned then. Coincidentally, text there looks almost like in Windows (and on my Mac indeed all text looks worse than both of those) :P

Michał Cichoń

FreeType require at least basic knowledge about typography how it apply to raster grids. People responsible for creating font rendering in our new engine are very creative on field of missuses.
I had to agree Linux (Gnome actually) has improved on field of text rendering.
Ok, maybe we should leave this as a digression. Because how exactly "good looking" text is defined depends from who is looking.
Please go back to performance problems :P

Elias

Edgar Reynaldo: I modified your example to do the cache timing twice and this is what it looks here:
{"name":"604237","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/2\/52dd016bca4c51b20edcb30f3e06ce72.png","w":810,"h":629,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/2\/52dd016bca4c51b20edcb30f3e06ce72"}604237

So yes, caching is slow. But at least it does seem to work and al_get_text_width() is fast once the glyphs are cached.

Peter Wang

The glyph caching in the TTF addon should probably be optimised by caching more than one glyph at a time, thereby removing redundant lock/unlocks. (I didn't check it though).

Edgar Reynaldo

There was a bug - I was adding the wrong variable to the second cache's total time.

After I fixed it, the second cache time was almost negligible. I also compared DIRECT3D to OPENGL and OPENGL_3_0 drivers :

Direct3D :

c:\ctwoplus\progcode\allegro5\test>A5Bouncer.exe
Average load time     = 1.278 ms
Average cache time    = 310.100 ms
Average cache2 time   = 0.021 ms
Average display time  = 0.202 ms
Test count = 100

OpenGL :

c:\ctwoplus\progcode\allegro5\test>A5Bouncer.exe
Average load time     = 0.639 ms
Average cache time    = 41.820 ms
Average cache2 time   = 0.014 ms
Average display time  = 0.295 ms
Test count = 500

OpenGL 3.0 :
Couldn't test, failed to create a display with this option on my laptop.

OpenGL is a little more than 7.5 times as fast as Direct3D when creating the initial cache of glyphs, but slightly slower to draw and faster to load.

Karadoc ~~

I just ran the test of my computer.
Average load time = 0.34 ms
Average cache time = 25 ms
Average cache2 time = 0.016 ms
Average display time = 0.065 ms

But... The main reason I'm posting is that the first time I ran it the average cache time was more like 300ms. It dropped dramatically the second time without me having changed anything. I can only guess that the drop was a result of some kind of Windows 7 automatic optimization.

(I assume it's using Direct3D.)

Arthur Kalliokoski

the drop was a result of some kind of Windows 7 automatic optimization.

I'd guess verdana.ttf was already in file buffers.

Elias

The glyph caching in the TTF addon should probably be optimised by caching more than one glyph at a time, thereby removing redundant lock/unlocks. (I didn't check it though).

Yes, if someone makes a patch for an extra al_cache_glyph_rage function as Edgar proposed it could do that. However I'm not sure it really would be faster. At least with OpenGL locking a bitmap region only locks that region (i.e. GPU transfer is limited to the locked area). Multiple calls therefore only save the lock commands themselves. Since the al_get_text_width("abc...") approach does not do any other OpenGL calls between the lock/unlock calls it might have little to no effect. The driver even might optimize it into a single lock already. But only trying it out would tell of course.

About the DirectX case, not sure why it's so slow. I don't understand how locking works there at all - if someone likes to read up on MSDN I'm sure we could make it as fast as OpenGL. If it's that every lock/unlock call has to download/upload the whole bitmap from/to the GPU then reducing the times the bitmap is locked will have a big impact there of course.

Peter Wang

I was thinking we'd just cache the new glyphs as a first pass over the string in the render() method, then draw them in a second pass. That way you can minimise the number of times you lock the cache bitmap. I had a quick try at this, but it's not a completely trivial change.

I think it would make a big difference. On the benchmark you posted, I can reduce the average cache time from 19.5 ms to ~13 ms just by reducing the cache bitmap size from 256x256 to 128x128. If I switch to using memory bitmaps, the cache time drops to 0.9 ms (obviously drawing time goes up).

Elias

Hm, I guess something with locking doesn't work the way I thought then.

Or maybe your numbers can be explained by the fact that we do an unconditional texture upload of the complete texture whenever a bitmap is first created. This is of course stupid. Whenever a 256x256 cache is created al_create_bitmap allocates 256x256 pixels worth of uninitialized memory and uploads them to a texture just to clear that texture immediately afterwards. Instead we should leave the texture un-initialized in the first place (pass NULL to glTexImage2D). (This will also speed up al_load_bitmap.)

Also, looking at the ttf code we do some stupid things during cache creation, for example:

 297                      unsigned char c = *ptr;
 298                      float cf = c / 255.0f;
 299                      *dptr++ = 255 * cf;
 300                      *dptr++ = 255 * cf;
 301                      *dptr++ = 255 * cf;
 302                      *dptr++ = c;

That's a useless divide, three useless multiplies and 4 useless float/char conversions for each pixel. Probably negligible performance-wise but still hurts my eyes :P

[edit:]

I changed both of those things, but neither made a difference.

Evert
Elias said:

Probably negligible performance-wise but still hurts my eyes :P[edit:]I changed both of those things, but neither made a difference.

The compiler may be clever enough to figure out that it should eliminate both the divide and the multiply. Maybe. Maybe not.
I wouldn't expect a massive performance boost either way, but yes, that sight of that code makes you want to slam you head against your desk (or it would, were I not on the sofa with a fever).

Elias

Ok, according to valgrind --tool=cachegrind 75% of all time is spent in glTexSubImage2d<-ogl_unlock_region<-al_unlock_region<-al_unlock_bitmap->cache_glyph<-render_glyph<-text_length<-al_get_text_width.

(92% of all time is spent in al_get_text_width - FT_Load_Glyph only takes up about 8%)

So yes, caching multiple glyphs at a time and thus reducing the calls to glTexSubImage2D seems to be the way to optimize this :)

Also interesting, half of the time spent in glTexSubImage2D is actually shown inside of memcpy - so it seems the first thing my OpenGL driver does is copy the passed memory somewhere else. Could also have to do with valgrind, everything runs very slow under it so its profiling results can be quite inaccurate.

Peter Wang

Ok, I'm working on it.

Thomas Fjellstrom

Yeah, one of the things you want to do with many OpenGL Drivers is cut down on the number of OpenGL calls as much as possible. Many of them do a lot of setup and tear down for one call, and it can add up.

Peter Wang

In 5.1 now:

{"name":"604282","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/e\/d\/ed92c4fa8cd621687df68979d49d84f8.png","w":800,"h":600,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/e\/d\/ed92c4fa8cd621687df68979d49d84f8"}604282

The bottleneck now is clearing new bitmaps(!). This is only a hack to avoid artefacts when OpenGL samples the pixels around the actual glyph. If I comment that out:

{"name":"604283","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/d\/7\/d753057ed86b6505a3c8db333762ae22.png","w":800,"h":600,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/d\/7\/d753057ed86b6505a3c8db333762ae22"}604283

(I don't understand the other timing differences.)
The clearing is avoidable, but is too tedious for me to do.

Edgar Reynaldo

WOW! :o

Any chance you can do the same thing for the Direct3D drivers?

Peter Wang

D3D should have benefited from the same change. Post your results for that?

Edgar Reynaldo

Downloading 5.1 from SVN...

I won't be able to test for a while - it's taking me longer to get setup than I thought.

Matthew Leverton
matthew@mercury:~/tmp$ ./font50
Average load time     = 0.476 ms
Average cache time    = 8.766 ms
Average display time  = 0.440 ms
Test count = 1012

matthew@mercury:~/tmp$ ./font51
Average load time     = 0.316 ms
Average cache time    = 4.946 ms
Average display time  = 0.586 ms
Test count = 1013

Looks like cache time has dropped but display time has increased (OpenGL). This is 5.0 compared to 5.1 SVN.

Peter Wang

Ah, that's not good. I modified the benchmark to better represent what a real program does, but the slowdown is still there (0.060 ms/call to 0.066 ms/call on my machine). The problem seems to be simply making the second pass over the string.

Since drawing speed is a lot more important than caching speed, I guess I'll have to back out that part of my changes.

EDIT: but we can still do it when the caching is done as part of computing text dimensions. Done in SVN.

Matthew Leverton

The latest SVN was even slower with the original test (~0.586 to ~0.644).

But with your test program, SVN is the same speed as 5.0.

Peter Wang

Try turning off cpu throttling. I wouldn't put too much stock in the original test anyway, at least the drawing part. It does too little of it and is likely impacted by all the other things going on.

ex_ttf should be more representative of real programs. On my machine it went from ~4800 FPS to ~5500 FPS (cpu throttling disabled). Most of that should be attributed to the improvements to al_get_text_dimensions.

Matthew Leverton

Manually maxing the CPU frequency didn't change things.

ex_ttf is slightly (<2%) faster in SVN than 5.0. (Integrated Intel GMA950.)

Peter Wang

Can you also test r14782 instead of 5.0? That's what I have been comparing. The 5.1 seems to have regressed slightly due to other changes.

Matthew Leverton

The latest is about 7% faster than 14782 on ex_ttf.

Peter Wang

r14533 is the culprit.

Quote:

Make al_compose_transform do a full 3d multiply. Add some possibly necessary (for 3d) bits to al_ortho_transform

Edgar Reynaldo

Okay, finally got all of A5 built from SVN.

I compared the results from 5.0.2 earlier to SVN r14784 (after your initial fix) and to SVN r14796 (latest) and these are the times I came up with :

All times in milliseconds
-------------------------------------------------------------
                |   load   |  cache   |  cache2  | display  |
-------------------------------------------------------------
OpenGL          |          |          |          |          |
----------------|----------|----------|----------|----------|
5.0.2           |    0.639 |   41.820 |    0.014 |    0.295 |
5.1SVN r14784   |    0.643 |   15.076 |    0.030 |    0.336 |
5.1SVN r14796   |    0.619 |   13.344 |    0.017 |    0.437 |
-------------------------------------------------------------
Direct3D        |          |          |          |          |
----------------|----------|----------|----------|----------|
5.0.2           |    1.278 |  310.100 |    0.021 |    0.202 |
5.1SVN r14784   |    0.680 |   86.996 |    0.034 |    0.197 |
5.1SVN r14796   |    0.653 |   32.468 |    0.020 |    0.199 |
-------------------------------------------------------------

Direct3D is looking really nice now, and the caching time is comparable with OpenGL. The cache times for the latest svns for OpenGL have definitely improved too.

The only thing that might have gotten worse is display times for the latest svn version for OpenGL.

Peter Wang

Thanks for testing. But I don't know what to think. Can you post results for ex_ttf?

Elias

r14533 is the culprit.

That should have affected OpenGL and D3D equally [1] Wonder what else (specific to OpenGL) made drawing slower.

Evert

What's the errorbar on those numbers?

Edgar Reynaldo
Evert said:

What's the errorbar on those numbers?

You mean the statistical variance? I have no idea. I ran the program until the numbers had pretty much stabilized. For OpenGL, it ran fast enough that it had made between 500 and 1000 tests. For Direct3D with 5.0.2 it probably only took 100 tests (which took about 30 seconds for it). The later Direct3D versions are probably comparable to the number of tests for OpenGL.

Thanks for testing. But I don't know what to think. Can you post results for ex_ttf?

Here you go :

                   |        FPS
-------------------------------
5.0.2              |  1000-1150
5.1 SVN r14784     |   900-1000
5.1 SVN r14796     |   975-1175
--------------------------

I did my best to give the most common ranges for the program. It would probably give better results if the fps was averaged.

Thread #607522. Printed from Allegro.cc