MacOS issue with loading font arrays

Started by Ashmoor, August 13, 2021, 23:17:06

Previous topic - Next topic

Ashmoor

I have the following code on MacOS:

Code (blitzmax) Select


For Local fntSize:Int = 0 To 72
Font_Ubuntu_M[fntSize] = LoadImageFont ("fonts/Ubuntu-M.ttf", fntSize, SMOOTHFONT)
Font_Ubuntu_B[fntSize] = LoadImageFont ("fonts/Ubuntu-B.ttf", fntSize, SMOOTHFONT)

Font_Diavlo_L[fntSize] = LoadImageFont ("fonts/Diavlo_LIGHT_II.otf", fntSize, SMOOTHFONT)
Font_Diavlo_M[fntSize] = LoadImageFont("fonts/Diavlo_MEDIUM_II.otf", fntSize, SMOOTHFONT)
Font_Diavlo_B[fntSize] = LoadImageFont ("fonts/Diavlo_BOLD_II.otf", fntSize, SMOOTHFONT)
Font_Diavlo_Book[fntSize] = LoadImageFont ("fonts/Diavlo_BOOK_II.otf", fntSize, SMOOTHFONT)

Next


After any 3 font arrays are loaded, images cannot be loaded anymore. LoadImage or LoadAnimImage no longer seem to work. I tried commenting all but 2 font arrays in the sequence above and everything works fine.

Why is this happening, what can I do to prevent it? The same code works fine on windows 10 x64.



Ashmoor

I'm adding more details, maybe someone can help.

Code (blitzmax) Select

SuperStrict

Framework SDL.gl2sdlmax2d
Import BRL.Retro
Import BRL.Font
Import BRL.Max2D
Import brl.FreeTypeFont
Import brl.jpgloader
Import brl.pngloader

Global fontDiavloBlack40:TImageFont
fontDiavloBlack40 = LoadImageFont("fonts/Diavlo_BLACK_II.otf", 60, SMOOTHFONT)

Graphics 800,600

SetBlend alphablend

'Global imgToLoad:TImage=LoadImage("graphics/dol_tex.jpg")  '<-------- This works

Global Font_Ubuntu_M:TImageFont[73]
Global Font_Ubuntu_B:TImageFont[73]
Global Font_Diavlo_M:TImageFont[73]
Global Font_Diavlo_Book:TImageFont[73]
Global Font_Diavlo_B:TImageFont[73]
Global Font_Diavlo_L:TImageFont[73]

LoadGameFonts()

Global imgToLoad:TImage=LoadImage("graphics/dol_tex.jpg") '<----------- this doesn't

Repeat
Cls
DrawImage(imgToLoad,0,0)
DrawTestText()
Flip()
If KeyHit (KEY_ESCAPE) Or AppTerminate() Then End
Forever

Function DrawTestText()
SetImageFont(fontDiavloBlack40)
DrawText("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",20,200)
SetImageFont(Null)
EndFunction



'Summary: Loads all game fonts
Function LoadGameFonts()

For Local fntSize:Int = 0 To 72
Font_Ubuntu_M[fntSize] = LoadImageFont ("fonts/Ubuntu-M.ttf", fntSize, SMOOTHFONT)
Font_Ubuntu_B[fntSize] = LoadImageFont ("fonts/Ubuntu-B.ttf", fntSize, SMOOTHFONT)

Font_Diavlo_L[fntSize] = LoadImageFont ("fonts/Diavlo_LIGHT_II.otf", fntSize, SMOOTHFONT)
Font_Diavlo_M[fntSize] = LoadImageFont("fonts/Diavlo_MEDIUM_II.otf", fntSize, SMOOTHFONT)
Font_Diavlo_B[fntSize] = LoadImageFont ("fonts/Diavlo_BOLD_II.otf", fntSize, SMOOTHFONT)
Font_Diavlo_Book[fntSize] = LoadImageFont ("fonts/Diavlo_BOOK_II.otf", fntSize, SMOOTHFONT)

Next

End Function



Same code runs just fine in windows 10.

Midimaster

this are 6*72 TImagefonts!!! Perhaps the MAC cannot handle such a big number.

May I ask a question? For what do you need a such big number of fonts at the same time. Are in your app textes  on the screen which use all 432 font at the same time together?

Maybe a solution or a workaround is to load only those 10 variants of fonts that are used at the same time?


...back from Egypt

Ashmoor

QuotePerhaps the MAC cannot handle such a big number.

That would be my guess, but it does not make any sense. I mean it can handle lots of images but not TImageFonts? I checked and it can only handle 231 fonts. However, if I don't use the SDL framework everything works fine. I don't even know where the problem is to report it.

I'm not using all those fonts at the same time but I use them to dynamically scale down text to fit my dialog boxes. The game will be localized in a few languages so I have to make sure  the text does not overflow.

How would you go about selecting a smaller font size at runtime?


Midimaster

did you already measure how long it really needs to load a complete set of all 6 fonts?
If it lasts too long for you, you could try to add the 6 source ttf-file via INCBIN and measure again the loading time.


Did you ever need a font in size 0? And perhaps it would be exact enough to have them with +20% size steps

you would need 16 sizes:   5 - 6 - 7 - 8 - 10 - 12 - 14 - 17 - 21 - 25 - 30 - 37 - 44 - 53 - 64 - 77

and even with 10% size difference you would only need 26 sizes:
5-6-7-8-9-10-11-12-14-15-17-18-20-22-25-27-30-33-37-40-44-49-54-59-65-72

so you could keep 12 active font variants in a TList. And when you need a new one you could remove an old one.
...back from Egypt

Ashmoor

Quotedid you already measure how long it really needs to load a complete set of all 6 fonts?
If it lasts too long for you, you could try to add the 6 source ttf-file via INCBIN and measure again the loading time.

No, what would be the use of that?

QuoteDid you ever need a font in size 0? And perhaps it would be exact enough to have them with +20% size steps

No, I obviously don't need a font in size 0. It was a mistake on my part. Your idea of 10% increments sounds like a good approach.

Still this sounds like an issue with Blitzmax. Can you try running the following code on your Mac? If you get more than 231, then it's clearly a hardware issue with my old MacBook.

Code (blitzmax) Select

SuperStrict

Framework SDL.gl2sdlmax2d
Import BRL.Retro
Import BRL.Font
Import BRL.Max2D
Import brl.FreeTypeFont
Import brl.jpgloader
Import brl.pngloader

Global fontDiavloBlack40:TImageFont
fontDiavloBlack40 = LoadImageFont("fonts/Diavlo_BLACK_II.otf", 60, SMOOTHFONT)

Graphics 800,600

SetBlend alphablend

'Global imgToLoad:TImage=LoadImage("graphics/dol_tex.jpg")  '<-------- This works

Global maxFontCount:Int=323
Global Font_Ubuntu_M:TImageFont[500]

LoadGameFonts()

Global imgToLoad:TImage=LoadImage("graphics/dol_tex.jpg")       '<----------- this doesn't

Repeat
        Cls
                DrawImage(imgToLoad,0,0)
                DrawTestText()                         
        Flip()
        If KeyHit (KEY_ESCAPE) Or AppTerminate() Then End
Forever

Function DrawTestText()
        SetImageFont(fontDiavloBlack40)
        DrawText("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",20,200)
        SetImageFont(Null)
EndFunction



'Summary: Loads all game fonts
Function LoadGameFonts()

        For Local fntSize:Int = 0 To maxFontCount
                Font_Ubuntu_M[fntSize] = LoadImageFont ("fonts/Ubuntu-M.ttf", fntSize, SMOOTHFONT)
        Next

End Function


iWasAdam

you should only load the fonts you want when you want them. There is no reason to load stuff just because you can

Ashmoor

you should only load the fonts you want when you want them. There is no reason to load stuff just because you can

I agree with that. How would you go about implementing it for a feature like the attached screenshot. Each dialog "bubble" has a text block that must fit inside. If it doesn't then it's scaled down and broken into lines until it does. I have around 60 scenes like this one and the text size is computed at run time, when a story scene is loaded. The game will be localized in 6 languages. The German version is probably the one with the longest texts.

Would I check font size 32, it does not work, then load font size 31, check again, may not work, load font size 30, etc. then move to the next dialog bubble and start over?

Derron

There are multiple options to achieve it:
- load font sizes in steps (1,3,5,7...13,15 ...) and maybe only the "default" ones in the designed font size (so it "fits"9
- maybe during initial setup try out the localized stuff for each scene and find out what fonts are needed (you will never need ALL - for specific localizations)
- fix the width and make height of the bubbles adjustable (or fix height and make width a bit adjustable)
- You might even play with the idea of a "virtual" font - not actually loading the font but having the glyph information cached (so you can calculate text widths and heights without ... much trouble)

Most promising: use an image font class not creating an image texture for each single glyph - 300 files with maybe 300 glyphs in it already make up 90.000 textures :)
Texture atlas ... am not 100% sure  but it sounds like it could help here.

https://github.com/TVTower/TVTower/blob/master/source/Dig/base.gfx.bitmapfont.bmx

I think I somewhen linked it for you already. It is my code to load TTF fonts into image atlases (saves textures) and also has functions for line wrapped / stylized (color, italic, bold ... shadow/glow) text in NG.


bye
Ron

Ashmoor

ok, will check all the options. For sure will reduce the amount of fonts used. It's just so weird that it works with standard brl but not with SDL.

Quote
I think I somewhen linked it for you already. It is my code to load TTF fonts into image atlases (saves textures) and also has functions for line wrapped / stylized (color, italic, bold ... shadow/glow) text in NG.

Yes, you did. But this game was built using Vanilla, I'm just now using NG to port it for MacOS. Will probably switch completely to NG soon.

Midimaster

I measured the loading of a TimageFont. First time loadfing needs less than 1msec. If you repeat the loading of the same font but with a different FontSize the time needed shrinks to 0.3msec
...back from Egypt

Ashmoor

QuoteI measured the loading of a TimageFont. First time loadfing needs less than 1msec. If you repeat the loading of the same font but with a different FontSize the time needed shrinks to 0.3msec

I still have no idea what to do with this information.

There clearly is an issue with SDL and TImageFont as we've ran some tests on discord yesterday and at some point the limit was broken then it came back on for no reason.

I have significantly reduced the amount of fonts loaded and for the next game I will optimize it even further. Your suggestion to use % increments is nice and Derron also gave me a few very good options @Discord. That along with his font class should greatly optimize the texts in my next game.

PS: I've written this message Monday morning but forgot to press "Post", sorry for late reply Midimaster

iWasAdam

QuoteI measured the loading of a TimageFont. First time loadfing needs less than 1msec. If you repeat the loading of the same font but with a different FontSize the time needed shrinks to 0.3msec
Whilst this is mostly irrelevant to the issue of using too many fonts.

Here's the actual actual answer to the (unwanted) speed:
1. When you fist load a font - the font is loaded into memory - and it will stay there until the garbage collector clears it,
2. when you create a font with a different size - the font is already in memory, and the new size is created from that - That is the reason it becomes faster.
3. this is because of the way blitz is programmed. it's been that way for a long time and is simple to understand.

OK.
@Ashmoor Listen to Derron - he really knows his stuff and what is going on underneath with blitz.
You mentioned that different languages needed different font sizes - then that is how you progress - test and find the optimal font for each language.

But... There is another way you could do this:
Render your fonts/text to an offscreen canvas that will support the text and then copy this canvas to your main canvas. resizing to fit. Derron can give you more advice on how to do this :)


Derron

rendering to a texture is not of much help - as this will scale width and height. but "width" is kind of constant (per screen) so if using a smaller font more text will fit onto a line - and this will lead to different parts of the complete text to be "wrapped" (line wrap).

Had this in mind too but scrapped it.


My idea was to use a dynamic "stepping" for the font sizes and its variations.
if your font was size 10 - also load 8 and 9 (step size 1)
if your font was size 20 - also load 16 and 18 (step size 2)
if your font was size 30 - also load 24 and 27 (step size 3)
The bigger a font the less pixels a "bit smaller" font size will save.
This is similar to the 10% thing suggested above already (did not read about it before trying to help). 10% is of course easier to calculate :)


A way more prolificient approach is to find out what fonts are needed on game start (if not cached for the choosen localization language already). The game knows potential dialogues ... and "layout" of the speech bubbles on the screen -- so it can calculate everything in advance.
This way it will only load the fonts it really needs.



BTW: the font Ashmoor was using contained 1200 glyphs ... and I sent him a font which only had 120 glyphs in it. then we checked if loading 3000 files instead of 300 lead to the same issue - it didn't.
We also loaded the font with very huge sizes so that the glyphs were 2k textures... no change. So the "issue" itself is still unresolved. We just circumvent it now by loading way less assets (think of 1200 glyphs * 250 font sizes ... all of them GC managed :)).


bye
Ron

Pingus

QuoteWould I check font size 32, it does not work, then load font size 31, check again, may not work, load font size 30, etc. then move to the next dialog bubble and start over?

This is somehow what I do with JM games (and it is not perfect as you know).
Another possibility is to use fewer and bigger fonts,  and scale them down depending on the cases. By example size 32 would be used for texts from 24 to 32, size 24 for 16 to 24... but that may blur the texts a little.

Maybe a reason why it crashes with SDL is related to the amount of memory used for this purpose ?
I guess that a tt font is converted at runtime in bitmaps and for a reason or another SLD limit that bitmap size, while directX would not care ? Just wondering...