MacOS issue with loading font arrays

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

Previous topic - Next topic

Derron

Quote from: Pingus on August 19, 2021, 01:25:18
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...


quoting myself (answer above yours).
Quote
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.


bye
Ron

Ashmoor

QuoteMaybe a reason why it crashes with SDL is related to the amount of memory used for this purpose ?

I don't think it's directly related to the memory but I'm not good enough to tell.

And I don't think it's the amount of glyphs in a font either. For example I just ran a test now on windows and the program halts after loading 510 font instances. One font has 1264 glyphs leading to a total of ~650k glyphs loaded, the other font has 223 leading to a total of 120k glyphs. They both halt after 510 instances.

This is the font loading function
Code (blitzmax) Select

Function LoadGameFonts2()
    For Local fntSize:Int = 1 To 1800

           'Font_Ubuntu_M[fntSize] = LoadImageFont ("fonts/Ubuntu-M.ttf", fntSize, SMOOTHFONT)    '<------- 1264 glyphs, loads 510 instances
           Font_Ubuntu_M[fntSize] = LoadImageFont ("fonts/Grande_Andretti_-_limited_charset2.ttf", fntSize, SMOOTHFONT) '<-------------- 223 glyphs, loads 510 instances


          If Not Font_Ubuntu_M[fntSize]
           Throw "Font load failed: fntSize=" + fntSize +". Loaded a bit more than " + fontGlyphCount + " glyphs."
        EndIf
        Print Font_Ubuntu_M[fntSize].CountGlyphs() + " glyphs loaded."
        fontGlyphCount :+ Font_Ubuntu_M[fntSize].CountGlyphs()
    Next   
    Print "loaded a total of " + fontGlyphCount + " glyphs."
End Function


Ashmoor

#17
However, Blitzmax is not consistent in itself. The first time I ran the code above today, from MaxIDE, I only got about 130k glyphs loaded from both fonts. Then I switched a bit between release and debug and got it back up to 510 instances.

Edit: I have 32 GB Ram in my system and 6GB video memory. I'll try to update to the latest blitzmax version from GitHub and will see how that goes.

Derron

You can try to run a debug build, debugstop before actually loading the 510th font instance. And then check if one of the elements becomes "null" albeit it shouldn't.


bye
Ron

Midimaster

#19
Quote from: Ashmoor on August 17, 2021, 15:20:13
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.
....

What I wanted to say is that your pre-loading is not neccessary. If you load the font in the moment, when you need it this would not cause timing problems.

If you want to test different sizes of a font you could always start with a first size, then decide if it is to big or to small. This would only need 1msec. Now you decide for the next size, load it to the same TImageFont and check again if it fits. This would need less than 1msec, because the font-file is still in the buffer. This means a repeat loading of the same font in different sizes would also not cause timing problems.

If you think about a strategy of loading I would not do this with a seqence like: 32, then 31 then 30... but:

Testing 32 Fonts from size 1 to 32

Start with size 16
Load this size
Decide wether it is to big or to small
If it is to big substract 8 -> test with 8
If it is to small add 8 --> test with 24

Load this size
Decide wether it is to big or to small
If it is to big substract 4
If it is to small add 4

Load this size
Decide wether it is to big or to small
If it is to big substract 2
If it is to small add 2

Load this size
Decide wether it is to big or to small
If it is to big substract 1
If it is to small add 1

Found!!!

This method only needs 4 checks to find the optimal font.


This can be coded very easy with a simple iteration.
...back from Egypt

iWasAdam

QuoteBlitzmax is not consistent in itself
You've always got to have the following clear in your head:
1. macos and window and linux, etc are not the same.
2. there is completely different code going on behind the scenes. this might be differences in logic, handling etc.
3. sometimes completely different libraries to do the same thing on different systems.
4. BlitzMax hides all of this from you and gives you a simple command
5. the os compilers can be and are very different
6. the hardware and os are very different beasts. macos can be more forgiving, and windows more strict
7. the memory handling between os is completely different

windows uses win32
macos uses - all sorts of stuff, cocoa for example. plus you are dealing with both c and objective c

The thing you HAVE to do is test on both systems and write code that is both clear and concise. and in some instances os specific!


Derron

Quote from: Midimaster on August 27, 2021, 08:35:30
What I wanted to say is that your pre-loading is not neccessary. If you load the font in the moment, when you need it this would not cause timing problems.

If you want to test different sizes of a font you could always start with a first size, then decide if it is to big or to small. This would only need 1msec. Now you decide for the next size,

[...]

This method only needs 4 checks to find the optimal font.

This can be coded very easy with a simple iteration.

Assuming this takes 1 ms per "font to load + to find out dimensions" it means you already spend 4 ms for identifying the font.
This needs - in worst case - to be done a multiple times on the screen (multiple dialogue boxes - each with other text and thus requiring multiple font sizes to be "checked").
If there are at the end 2 boxes with "totally different font sizes" (so not loaded already) this will already be 8 ms just for "font stuff".

Yet you might require more than 1 ms:
- load font (decode ttf, rasterize glyphs)
- do the text wrapping based on the glyphs

Somewhen you reach a required time leading to a visual "hickup". So this most probably only works satisfying if you do fade-to-black screen transitions or static imagery with no animation.


Best approach stays the identification of "to use font sizes" at the begin of the game / first use of a language. Especially if your dialogue texts are "fixed" and not containing dynamic information (individual character name, item names, day names ...) as this means the line wrapping information is "constant" and can be precalculated then.
Next best bet is to scale the bigger fonts - or to "know" what font sizes are "realistically" used. You wont need "font size 200". You might need "10,11,12" for "text", "24,25,26" for headers etc.
This will result in maybe 20 font sizes at the end. Multiplied by "bold/italic"-style variations


BTW another approach to "fit" smaller font sizes is to slightly increase "word spacing" ("width of a space-char") and glyph-advancement (of course you here better have your custom font draw class). This way you can slightly "increase" visual dimension of the text without falling back to a "bigger" font.
Maybe - only some percents - this can also be done to "scale down" (make chars a tiny bit more "narrow" etc). Without affecting the visual appearance of the glyphs itself (they are not scaled - just the whitespace is manipulated).



bye
Ron

iWasAdam

QuoteBTW another approach to "fit" smaller font sizes is to slightly increase "word spacing"
or just write a dirty text render system, where you draw each character giving space. if you already know which languages will be 'longer' then you could also do this with a different font.

BUT...

If you already know how many languages and which are causing issues then just tailor the output for each language. load the needed fonts for each language when the language is picked (use flags for the language picker) and virtually forget most of this thread - brute force is always the best way ;)

Midimaster

normally texts stay at the screen for a couple of seconds. So you need not to proceed the same testing each FLIP. If you need to display new text, you could check the text once and load the font and asign it to the box. If BoxFont<>NULL means "text is already checked, use this font!"

I testet that finding out the best size does not need more than 2msecs. Each current text box should have its own BoxFont
...back from Egypt

Derron

Quote from: Midimaster on August 27, 2021, 11:26:34
normally texts stay at the screen for a couple of seconds. So you need not to proceed the same testing each FLIP. If you need to display new text, you could check the text once and load the font and asign it to the box. If BoxFont<>NULL means "text is already checked, use this font!"

I testet that finding out the best size does not need more than 2msecs. Each current text box should have its own BoxFont

Nobody wrote you should do that each frame. But you wrote about doing it "on request" (or "when needed"). Which means it could happen during an animation (balloons with text pop in ...). If then such a lookup takes longer (no SSD but classic HDD, bigger font files - he uses a one with 1200 glyphs needing rasterization) it can lead to a little "hickup".
This is just a "minor" thing - of course.


Essence of what we all replied is surely: only load the fonts you need - instead of 250.
Yet it still indicates there is a "bug" somewhere - and maybe a bug which does not only affect fonts but maybe other things too. Hence the request to debug the font loading on his side properly.


bye
Ron

iWasAdam

Check which font loader Blitz uses. I've had issues with freetype font on numerous occasions going back many years.

There is also the fonts themselves - it only takes one wrong glyph to throw the entire font off. Mac and PC handle fonts very differently

So Derron is (again) correct, with the possibility of further bugs deep within the system.

As an aside to this - wonkey had a font issue that caused very strange memory issues and wonkey/monkey are derivatives of BlitzMax.

My own take would be to load minimal fonts and potentially rewrite the output routines - say one font of a bigger size that can be scaled simply down. Marks way of loading fonts is not (imho the best way to approach things, best to keep fonts to a minimum) a brilliant solution - lots of TImage plus memory overheads.

Derron

The loads of Timages allow to circument an issue "easily": maximum texture size supported by ones GPU.

My font class places all the glyphs on a single texture - so at a font size of 200 it surely would run into issues with maximum texture sizes here and there. You might need to have multiple atlases then (so at the end you might have 10 atlases with 20 big glyphs each - to have all your 200 characters rasterized and available).
Once I found the time to make my font class independent from my DIG-framework (means custom FontSprite, FontSpriteAtlas types etc) I could easily add this too (as the glyph does not need to know where their texture actually comes from - ist has a "draw(x,y)" and whatever happens in the background is not of their matter :)).


bye
Ron

Ashmoor

Thanks for all the replies guys. Currently I am too busy to find a two hour window to run better tests. I will try to do it tomorrow and maybe we'll get to the bottom of this.

@iwasadam when I said it's not consistent in itself I meant within the same system. When I tested the loading of fonts I did what Derron advised and added a debug stop before loading the crashing instance and went step by step after that. The program didn't halt. I stopped it and ran it again without the debugstop line, it loaded the fonts without issue. I tried with 2000 instances and it worked. I closed MaxIDE and started it again and then the same code, that didn't crash last time would crash again. I need to record my screen while running the tests so I can be sure that I don't set some flags differently.

Derron

If you think it is Blitzmax creating "varying" code, then just run the same binary over and over (instead of creating/compiling to a new binary each time).

The issue might still be with RAM/VRAM, memory pages, ... it might even be some race condition / threading (dunno how freetype loads fonts).


bye
Ron

Ashmoor

I'm trying to add all the latest updates from github but I'm getting an error "Can't balance types Byte Ptr and LPARAM", pointing to line 532 if win32maxguiex.bmx

I got BlitzMax_win32_0.129.3.45.7z for win32 x86/x64

got bmk and bcc from brucey's github

copied bmk and bcc to "src"

used command prompt to build them "bmk makeapp -a -r -h -t console ../src/bmk/bmk.bmx" and "bmk makeapp -a -r -t console ../src/bcc-ng/bcc.bmx"

copied resulting exe files to "bin"

copied new modules into "mod" folder ( brl.mod, database.mod, maxgui.mod, physics.mod, pub.mod, sdl.mod)

restarted maxIDE and went to program/rebuild all modules.

I must be doing something wrong. This is on windows 10 x64.