|
Slowness when drawing fonts |
AMCerasoli
Member #11,955
May 2010
|
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
|
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
|
Hmm, why are they not cached when the font is loaded? -- |
Todd Cope
Member #998
November 2000
|
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
|
Ah, so it just caches those characters you're using when you first draw them. -- |
AMCerasoli
Member #11,955
May 2010
|
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... 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... 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 Because caching cannot be performed in both an easy and The hows and when of caching are explained in the "glyphs" 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
|
AMCerasoli said: 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.
Mark Oates said: Ah, so it just caches those characters you're using when you first draw them.
-- |
Thomas Fjellstrom
Member #476
June 2000
|
AMCerasoli said: 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
Member #358
May 2000
|
Even faster would be caching whole words or the whole text, when possible. -- |
AMCerasoli
Member #11,955
May 2010
|
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. Thomas Fjellstrom said: 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
|
AMCerasoli said: 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.
>_< Quote: Yes but it's doing it wrong. Exaggerating it's like loading the bitmap before you want to draw it.
... like I said. -- |
AMCerasoli
Member #11,955
May 2010
|
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
|
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
Member #358
May 2000
|
AMCerasoli said: 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
Member #11,955
May 2010
|
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.
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: www.fifi.org said: 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.) -- |
AMCerasoli
Member #11,955
May 2010
|
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? -- |
Thomas Fjellstrom
Member #476
June 2000
|
AMCerasoli said: 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
Member #1,786
December 2001
|
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
|
How long do we have to wait before we release 5.2 .
|
Matthew Leverton
Supreme Loser
January 1999
|
Trent Gamblin said: 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
|
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
|
Matthew Leverton said: 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:] -- |
Edgar Reynaldo
Major Reynaldo
May 2007
|
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. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
|
|