Font runtime rendering?

I’ve just created a font from 8-308 pixels (and yes, i’m going to need a font of 300px for higher resolutions) with an increment of 10 pixels. This adds 70mb to the distribution of my game and is totally unacceptable. Among desktop targets I want to target Android + IOS, so I want to keep the distribution as small as possible! My graphic assets are only 55mb at this moment. So adding a extra 70 just for fonts is ridiculous. .

Also this is not the only font I want to import. So that would be 70 * number of fonts = ridiculous.

Before deciding on going up in 10 pt increments, maybe do some experimentation to see how well the scaling works. You might find you need significantly less increments, especially as you go up in font size since, when you have more pixels to work with, the result of scaling is usually better.

If you’re not a fan still that’s cool, it’s all about your needs. Definitely look into runtime font compilation, just make sure you unload fonts you’re not using anymore to keep your memory footprint down. I’m not well versed in such structures, but maybe some kind of frequency based cache approach? I dunno… all I’m saying is that if you’re worried about 70 fonts taking up a lot of memory, it’s going to be much, much worse if you’re dynamically loading fonts during run-time.

Also, for what it’s worth, this is no different than if you just loaded them from a stream. Every time you load a font it’s going to allocate some memory. Unless you’re requesting an unload of these assets, they’re going to pile up at runtime. Same with textures and audio… those just get loaded less frequently since for fonts, you’re going to be loading a new one every time you want to scale. So yea, this issue has nothing to do with the content pipeline… in fact, the content pipeline is actually saving you memory by doing a better job of compressing assets for the target platform (per one of the links posted in that github issue you linked above).

I basically do this with everything… So why not with the fonts? I even went as far to create a pooling system so that my objects can be requested and released (to the pool) in order to not keep creating new Garbage(); Which is a major optimization for recycling objects and ducking the Garbage Collector. Also I have a very sophisticated Atlas that shares as much sprites/animations/tilemaps on one texture by looking at the max_texture_size of the device at runtime. I want to do same things for fonts, but if there’s no API for generating them then I cant do that.

I absolutely disagree! It has everything to do with the Content Pipeline. And also you assume too much. I’m not going to use the 70 generated fonts, just a sub-selection of them. But, I will only know on runtime - per device - what the sub-selection will be. So in order to have them all available I have to generate them all so that i can accommodate all devices.

Resolutions that i wish to support go from 480 pixels up to 2160 pixels in height plus some world scaling set per resolution. This will end up in just a couple of font-sizes that i need - per device. But again, I will only know this at runtime.

Since you think its not the Content Pipeline, then by all means explain how this scenario is suppose to work with it…!?

First of all, It introduces a lot more of storage of the xnb files. Example: fa308.xnb = 8.198mb, the font file is 120kb. So that’s a big price tag on the storrage. And for a platform that’s also targeting smart devices it seems out of place… Secondly, “You are going to use RAM anyways…”. As long as everything is nicely cleaned and disposed, I really don’t think it’s much of a argument. As soon as the font is on the texture (VRAM), then you just dispose() the ram.

The only real argument is that it reduces runtime processing. But I do these things on initialization, not on updates so I really don’t care that some splash image is showing a whole extra second!

Also, this is the first framework/engine/library that I have encountered that does not allow you to set the font-size on runtime… We are programmers, not designers if you know what i mean.!

Neat :slight_smile:

Using the content pipeline to dynamically create a spritefont for you at runtime doesn’t really feel like it’s going to do anything terribly differently than a SpriteFont.FromStream method would do. It’s still got to build a texture containing all of the characters you want from the specified font. It’s going to store it completely in memory without generating the xnb file, but still given that you can delete that file later, it shouldn’t be all that bad. It’s a hoop you have to jump through because you have to write this code yourself instead of having it supplied from MonoGame, but I dunno, it doesn’t seem that big of a hurdle, especially since it sounds like you’ve got a mechanism in place for managing assets.

It’s not ideal for your case, but the reasons for why MonoGame stuck with the Content Pipeline seemed reasonable. They also provide the tools you need to work around this, if you so desire.

I’m wondering if this might not be the best idea. Assuming for a second that you’re not going with a breakpoint approach and instead do pure dynamic loading, this is a lot of fonts to pre-load! I can see your concerns. Out of curiosity, have you done any experimentation with some kind of “just in time” loading? Loading everything at once might add that extra second or two to an initialization, but if it’s a few milliseconds inside a single update among many to load just the font you need as you’re drawing, would that be so bad?

This is speculation of course, I don’t know the answers to these questions. Definitely things that might be worth an experiment or two :slight_smile:

I can definitely think of a lot of cases where the font you want isn’t known at compile time. I do understand why you want this functionality and I agree that it would be nice, but I also understand that for most games, knowing your font ahead of time is actually pretty reasonable. It’s only when you want to create supporting applications/designers for game assets that this becomes a bigger issue.

Most of my other experience is in using stuff like OpenGL (which I don’t think has any direct built-in text support), or looking at engines like Unity. For the latter, from what I can see, they’re doing all the stuff that you would be building for you… but I don’t think Unity and MonoGame are the same kind of thing.

@Trinith Thanks for the reply!

Excactly! As soon as its on the texture (VRAM) you can dispose the ram copy!, So process the font, feed the Texture2D and then dispose() the ram. This will not have no lasting impact on the ram. It can all be done in the same function.

Too me? Yet it does. :smiley: If you know in advanced about the font’s properties then just render it in advanced. And a couple of milliseconds can be a lot on a busy update/draw ecosystem. I always try and keep an eye out for performance: Only use new() in constructors, try and use StringBuilders where possible, use Pooling where possible, Texture Sharing and anything else I can think of.

But back to the topic, is “MonoGame.Framework.Content.Pipeline.dll” available on all platforms? I’m asking because it’s written for Desktop usage. It just might have some foreign code or API calls that are not supported everywhere. It would be a shame diving into it only to find out it will not work on Android/IOS/WindowStore.

I was thinking to just wrap some old c++ code I have for rendering fonts based upon stb_truetype. But while setting up for windows desktop I was fearing that it might not work on Android & IOS. I basically have no clue how to set up c++ for usage in C# on those platforms. It’s worth checking out though.

Changes are that i’m still going to use “breakpoints” solution. In which case I will just accept my personal discomfort with it.

Anyways, thank!

Just found out that the answer is no!. Its only for Desktop (Windows, Mac, Linux). Not for Smart Devices

So that options is no good for me. Its either the “breakpoints” solution or introducing a 3trd party library. This explains why there is no font builder at runtime… It’s probably a foreign library.! or API (eg: GDI) which will not work on all platforms.!

Definitely important! On the flip side though, from experience, I’ve found I can get caught in a performance trap. I’ve over-engineered stuff in the past and spent too much effort on something I didn’t need. Lately I’ve been trying to follow a more balanced approach.

I’m not saying that’s the case here, just that if the overhead of dynamically loading a font on demand during a single update, then caching it for all future updates, might actually fit within your performance expectations. Only you will know what’s best for your application :slight_smile:

Out of curiosity, what are you building?

I’m not sure. I’m actually interested enough in this topic that I started to do some research (then rather forcefully pulled myself back to focus on the task I’m working on, lol). I found that in the old XNA environment, this was definitely coupled to the Windows platform. I’m not sure about MonoGame though since it does do things a little differently. If MSBUILD is involved in the process, you might be in trouble for cross platform. You’d need to do some digging.

Multi platform 2D game engine for hosting my own games. Primary targets are Smart Devices and Desktops. I’ve started out with C++ from scratch, but a engine like that is too much work to also develop my own games for it. After that I tried Haxe. Was very impressed by the language and portability, but was less impressed by the rendering performance. Then I did some minor tests on Unity, libGDX and MonoGame. Unity was nice but under performing on Smart Devices (atleast to my own standards) GDX was not bad and a potential candidate, but I personally prefer C# over Java. Also, MonoGame has the out-of-the-box solution for DirectX render for windows and WindowsStore support.

I’ve been add it now for 2 months. The majority for the Graphics, Logic, Physics and Hierarchy (Scenes, Layers, Objects, etc) is about done. Still need to do Audio, Platform Specifics for Mac & IOS and FONTS :smiley:

I have a build system made in LUA that builds the games out of templates and creates my assets. While making that I didn’t figure that the fonts would form a problem :smiley: If i’m going to use the Content Pipeline I probably am going to generate the .spritefont files from LUA based on a configure file that all my games have.

Otherwise use C++. But since I’m new to Xamarin/Mono/MonoGame i’m not to eager to jump into that boat :wink: It’s not the C++ what i’m afraid of, but integrating it on all platforms can be a pain!

But, thanks for asking! And I also like to know what you are building?

Cool :slight_smile:

For me, I’m doing somewhat similar actually but going about it in a somewhat different way. I just picked some fairly simple game project and used that to direct my efforts. Anything I need to accomplish the game, I create. If I don’t need it, I don’t make it… I’ll do that when some future title demands it.

It’s going well, but taking forever! Mostly because I decided that, at the outset of this project, I wanted to make everything fully unit tested. The downside to this approach is, as I said, the amount of time it takes. I tend to follow a prototype and port approach so I can figure out how I want to do something, then turn that into testable code. Prototypes often take very little time, but moving over to “production” code takes much longer. The upside though is that in making sure everything is testable, it leads me to make much better code design decisions than if I just wrote it without focusing on testing. It’s also saved me a ton of time on some refactor work I’ve needed to do, since my tests really help me direct my attention to areas that break when I make changes.

I’m almost done my first title and have plans to move on to another one after that, but we’ll see how it goes. Mostly this is a “life goal” endeavor as well as hoping I can build some good experience, and maybe make a little bit selling copies if possible. The first title is just a simple app based on a board game called Mastermind. It’s not terribly complex but was a good starting point to drive my engine requirements. If you’re at all interested, you can check me out on Facebook here: Redirecting...

Sorry friend… I’m not on FaceBook… But board games can be fun to code. Thanks for sharing, Hope it works out for you!

I’m curious about the latter. How does Unity and Microsoft Word handle adjusting font size easily?

@Kimimaru Fonts are rendered the same everywhere. The the font is rendered by request with size and decoration parameters. That creates a bitmap containing the glyphs and an atlas of rects describing it. (then on a GL/DX environment the bitmap is loaded as a texture on vram) When you want to print a string the function that does that iterates over the characters and foreach character it finds the belonging rect will be copied to the screen/buffer.

Basically the content pipeline creates the texture and the atlas on compile-time. This disallows you from creating it on runtime. After researching this a bit, this is probably because they are using a third party (c++) library, or API calls (eg. GDI) which are not supported on all platforms.

So either introduce your own font renderer (I personally would go with stb_trutype), or scale the pre-processed spritefonts with the spritebatch.

You can use the alternative I use.

I gave up with spritefonts for anything other than debug a long time ago. Instead I swapped to signed distance field fonts.

There is a freeware tool available (sorry cannot remember the name of it at the moment) which generates the required texture and supporting text file.

It requires a renderer that can render quads at floating point positions, so you cannot use spritebatch, but that is trivial to create.

Then a custom shader. Which is simple.

`float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
float mask = tex2D(tex,input.UV).a;
float4 clr;

clr.rgb = colour.rgb;

if( mask < 0.4 )  
	discard;
else  
	clr.a = 1.0;

// do some anti-aliasing
clr.a *= smoothstep(0.125, 1.00, mask);

return clr;

}

`
The good thing is that the font can be scaled at runtime to pretty much any size you want.

You can also change the shader to do things like shadows, glows, borders, all sorts of things to liven up you text rendering.

If you think it is worthwhile I could package my stuff up and publish it somewhere, but it is really easy to do.

Anyway, might be a way to solve your problem quickly.

Good luck.

Stainless

1 Like

@StainlessTobii
Hi,

Thanks for your response! Your sollution seems pretty decent! But since my last post i’ve already implemented the “break point” sollution in my game engine with support for:

  • Single Line
  • Multi Line
  • Render in Rectangle Area’s
  • Alignments (left, center, right) (using MesureString())

The resolving text is integrated in my engine eco-system and it would be a shame making it this nice and then not use it! But your solution still might help others, perhaps you can still make it available on request for others!

Thanks a lot for sharing!

It requires a renderer that can render quads at floating point positions,

I dunno about the loading but spritebatch itself internally renders all the glyphs as quads.

When you make a call to spritebatch’s drawstring the letter evaluates to a rectangle that is stored in the spritefonts glyphs but that rectangle is divided by the images width and height and is turned into a floating point quad.

You can actually peel out the glyphs redo them and replace them along with the texture in the spritefont.
If you were so inclined to go thru all that.
In the case of getting rid of different sized fonts and just loading the largest ones basically you could then just rescale them down in both the glyph and texture with get set data and create them on the fly i suppose. You would have to keep track of the textures you created to dispose of them though on game end.

Hi willmotil,

You don’t have a method on spritebatch that takes a RectangleF as input, so what happens is the glyphs are slightly out of position and you get little gaps in words that just look awful.

A quad renderer is so much easier to work with, and you can batch it just like spritebatch.

Using signed distance field fonts you only have, want, and need a single texture. That’s a big thing if you want multiple font faces in your game. The less texture space we use for font rendering the better in all games, then we can have 4K textures where we really need them, like skin shaders.

I will see if I can strip my stuff out into a demo and publish it somewhere.

You don’t have a method on spritebatch that takes a RectangleF as input, so what happens is the glyphs are slightly out of position and you get little gaps in words that just look awful.

Are the physical pixels that make up your screen or your image described or stored discrete units ?.. yes they are so they ultimately equate to integer values. The spritefont.xml that the pipeline produces has a field that allows you to turn on kerning which you should do.

A quad renderer is so much easier to work with, and you can batch it just like spritebatch.

That maybe true but have you compared the raw speed you get with your renderer to spritebatch drawstring ?.

Using signed distance field fonts you only have, want, and need a
single texture. That’s a big thing if you want multiple font faces in
your game. The less texture space we use for font rendering the better
in all games, then we can have 4K textures where we really need them,
like skin shaders.

Well im not familar with signed distance fields.
If the goal is to eliminate loading multiple resolution fonts with just one im all for that.

The spritefont class is intended for speed it was never intended to handle a game that wants to use 10 different fonts. That said if you want this in monogame we would first have to know how to incorporate it into spritebatch and if it is possible jjag or nkast did the optimizing on spritebatch i did some initial work on unrolling the matrices but i put up all my working code with simplified comparisons and demo examples. It’s open source meaning anyone can contribute to limits of there ability to do so.

Like i said though internally spritebatch is just using what amounts to floats,

In truth using a rectangleF is a non issue once you realize all textures have pixel width and height and every quad translates to physical pixel coordinates.

/// <summary>
/// Submit a text string of sprites for drawing in the current batch.
/// </summary>
/// <param name="spriteFont">A font.</param>
/// <param name="text">The text which will be drawn.</param>
/// <param name="position">The drawing location on screen.</param>
/// <param name="color">A color mask.</param>
	public unsafe void DrawString (SpriteFont spriteFont, string text, Vector2 position, Color color)
	{
    CheckValid(spriteFont, text);
    
    float sortKey = (_sortMode == SpriteSortMode.Texture) ? spriteFont.Texture.SortingKey : 0;

    var offset = Vector2.Zero;
    var firstGlyphOfLine = true;

    fixed (SpriteFont.Glyph* pGlyphs = spriteFont.Glyphs)
    for (var i = 0; i < text.Length; ++i)
    {
        var c = text[i];

        if (c == '\r')
            continue;

        if (c == '\n')
        {
            offset.X = 0;
            offset.Y += spriteFont.LineSpacing;
            firstGlyphOfLine = true;
            continue;
        }

        var currentGlyphIndex = spriteFont.GetGlyphIndexOrDefault(c);
        var pCurrentGlyph = pGlyphs + currentGlyphIndex;

        // The first character on a line might have a negative left side bearing.
        // In this scenario, SpriteBatch/SpriteFont normally offset the text to the right,
        //  so that text does not hang off the left side of its rectangle.
        if (firstGlyphOfLine)
        {
            offset.X = Math.Max(pCurrentGlyph->LeftSideBearing, 0);
            firstGlyphOfLine = false;
        }
        else
        {
            offset.X += spriteFont.Spacing + pCurrentGlyph->LeftSideBearing;
        }

        var p = offset;                
        p.X += pCurrentGlyph->Cropping.X;
        p.Y += pCurrentGlyph->Cropping.Y;
        p += position;

        var item = _batcher.CreateBatchItem();
        item.Texture = spriteFont.Texture;
        item.SortKey = sortKey;
    
        _texCoordTL.X = pCurrentGlyph->BoundsInTexture.X * spriteFont.Texture.TexelWidth;
        _texCoordTL.Y = pCurrentGlyph->BoundsInTexture.Y * spriteFont.Texture.TexelHeight;
        _texCoordBR.X = (pCurrentGlyph->BoundsInTexture.X + pCurrentGlyph->BoundsInTexture.Width) * spriteFont.Texture.TexelWidth;
        _texCoordBR.Y = (pCurrentGlyph->BoundsInTexture.Y + pCurrentGlyph->BoundsInTexture.Height) * spriteFont.Texture.TexelHeight;

        item.Set(p.X,
                 p.Y,
                 pCurrentGlyph->BoundsInTexture.Width,
                 pCurrentGlyph->BoundsInTexture.Height,
                 color,
                 _texCoordTL,
                 _texCoordBR,
                 0);
        
        offset.X += pCurrentGlyph->Width + pCurrentGlyph->RightSideBearing;
    }

		// We need to flush if we're using Immediate sort mode.
		FlushIfNeeded();

}

No mate, you have got it all wrong.

Say I am displaying a string “Hello”

In the font definition the width of the H character is 16.25 pixels. I draw the H glyph in the correct place, then come to draw the e glyph.

Using spritebatch I would have to display it 16 pixels away from the start position, and the space between the chars is reduced. This can even result in glyph’s overlapping.

Using a glyph batch, which is just as fast as spritebatch, the glyph is in the correct position, just one of the pixels gets written to twice to handle the overlap when we get to physical pixels.

However the very nature of signed distance fields means this is not visible.

When I first wrote a SDF font renderer, I thought like you, but when I actually tested it, the result was garbage. It took me a couple of hours to figure out that problem. I also screwed up by having point sampling enabled, but that’s another story. :smile:

I have finished a quick demo which shows 4 different shaders (normal, inverse, shadow, and outline) working with the same two SDF fonts.

Displaying at 4 different font sizes.

Just got to find somewhere to put it.

Okay decided just to stick it on my website.

example project

I use google drive its good for small projects.

Anyways i still don’t see the big advantage. Or any problem with glyph’s overlapping, using spritebatch.
My complaint with spritebatch isnt quality its the lack of automatic mipmaping when scaling down.
But that is being pretty nit picky.

Its like a 45 point sized font (but the bigger the better with spritefont), that is resolution scaled via a design time w h proportion + user scaled. With user resizing on in a shrunk window.

Looking at that code i doubt its anywere near as fast as spritebatch. You would have to do some testing to make that claim you might be suprised what you find.

You can send in effects to spritebatch begin.
As well as a matrix, meaning you can use it for 3d text. ect…

Also as far as garbage all numerical text will generate it.

            spriteBatch.Begin();
            GraphicsDevice.SamplerStates[0] = new SamplerState() { Filter = TextureFilter.Point, AddressU = TextureAddressMode.Clamp, AddressV = TextureAddressMode.Clamp };

            Vector2 txPos;
            Vector2 txSize;

            txPos = VirtualPosToScreen(.025f, .33f);
            txSize = ScreenReScale(.5f ,1f);
            spriteBatch.DrawString(regfont, "Verdana mg scale .5, 1f", txPos, Color.Moccasin, 0f, Vector2.Zero, txSize, SpriteEffects.None, 0f);

            txPos = VirtualPosToScreen(.025f, .45f);
            txSize = ScreenReScale(.5f ,.75f);
            spriteBatch.DrawString(regfont, "Verdana mg scale  .5, .75", txPos, Color.Moccasin, 0f, Vector2.Zero, txSize, SpriteEffects.None, 0f);

            txPos = VirtualPosToScreen(.025f, .55f);
            txSize = ScreenReScale(.5f ,.5f);
            spriteBatch.DrawString(regfont, "Verdana mg scale .5, .5", txPos, Color.Moccasin, 0f, Vector2.Zero, txSize, SpriteEffects.None, 0f);


            // draw the font.
            spriteBatch.Draw(regfont.Texture, new Vector2(400, 400), Color.Blue);

            spriteBatch.End();

Dunno how you can draw bottom up ah that seems so un-natural to me.

        Vector2 VirtualPosToScreen(float x, float y)
        {
            var v = new Vector2(x, y);
             return v * new Vector2(GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height);
        }
        Vector2 ScreenReScale(float x, float y)
        {
            var v = new Vector2(x, y);
            Vector2 screenScaling = new Vector2(GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height) / new Vector2(800f, 600f);
            return v * screenScaling;
        }
        Vector2 ScreenReScale(float s)
        {
            Vector2 screenScaling = new Vector2(GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height) / new Vector2(800f, 600f);
            return s * screenScaling;
        }

Sorry im not trying to put it down or boast about the current spritebatch its got drawbacks, im just saying this.

That is nice but its not as simple or functional and i doubt its as fast as the current implementation and adding it in would be a lot of work to bring it on par with spritebatch, more then what is shown in that project far far more.

The image you have posted shows the massive problem that stops me using this technique.

Scaling artifacts. They are everywhere. Which for me makes it un-usable for font rendering.

The beauty of Signed Distance Field Fonts is you don’t get scaling artifacts. You get crisp clean edges. You do get a certain amount of fading out of text when you down sample a lot, but it’s still crisp and clean.

We can argue about what is faster, a quad batch or spritebatch all day. Maybe that is a good argument we should have somewhere. We may come up with some ideas for speeding up spritebatch, or getting rid of scaling artifacts, or something. I don’t think this thread is the right place for that though. ( I also think it’s going to come down to wavefronts and other GPU specifics which don’t fit the Monogame ethos… but that’s another topic). :smiley:

Anyway, anyone interested in font rendering now has a thread to read :grin: