All About Text - Best Practices

Dear developers, recently we get various reports about text rendering issues on production. While we continue to work on optimizations for general text rendering, we’d also like to give some tips for better performance that will always be applicable.

Types of Text Issues

Text constantly flickering

When rendering text, font rasterization is usually involved. This is the process of creating a bitmap image from a vector description of a glyph (letter/symbol) in the form of a TrueType font (.ttf). Since text needs to be crisp, these images are always made at the exact size they will be rendered at, to avoid scaling artifacts. This means that there can be multiple images for the same glyph, at different font sizes. The size is also affected by your device’s pixel density (DPI scale), which can be higher on mobile devices.

These images are all stored in one large texture, called the font atlas, that’s used for all text rendering. When this texture runs out of space, you will start seeing artifacts like text on screen constantly flickering. This occurs most often on mobile, where devices have a higher DPI scale.

This flickering happens because when the font atlas gets full, it will delete old glyphs to make room for the new ones. If there are too many glyphs at once though, there might not be enough space to fit all of them.

To avoid this happening, it’s best to avoid using too many combinations of glyph, font, and text size at the same time. A lot of the best practices listed below can help with that.

Constant re-layouts that take up considerable time

Sometimes there are profiler labels called “Text Shaping + Layout” that show up in the microprofiler that can take a lot of time each frame. This usually happens when:

  • Many scale-sized BillboardGui objects containing text are shown on screen at the same time.
  • Typewriter animations that are constantly changing text content being used on multiple instances at once.
  • Tween animations that are constantly changing text size.

Relayouts only occur when something in your GUI has changed. This can be from a script, a TweenService tween, from the camera moving (with BillboardGui), or from the player interacting with the GUI.

Best Practices for Text

Set MaxDistance on BillboardGui

A common issue we see in games is having dozens, or even hundreds, of BillboardGuis being rendered that aren’t even visible on the screen, but still impact performance. This is often because they’re in another room, but are occluded by a wall.

Setting the MaxDistance property will cause the UI to stop rendering when it’s too far away from the player, which should help reduce the number of BillboardGuis being rendered at once.

Choose smaller text sizes in SurfaceGuis

Instead of setting TextSize to 100 on your SurfaceGuis to make the text bigger, consider lowering the PixelsPerStud property on your SurfaceGui. This will make all Offset sized objects in that SurfaceGui larger, and can allow you to use a smaller TextSize, saving space in the font atlas.

Avoid TextScaled when possible

The TextScaled is useful for making sure that text will fit in your game, but it comes with a performance and memory cost. You should consider making the text size fixed and using AutomaticSize instead.

A common belief is that you have to use TextScaled for text to be readable on phones, but this isn’t the case. DPI scaling makes sure that sizes of 20 or above are readable no matter what device you’re on.

TextScaled takes about 10x more time to layout, and this impact can be visible in the microprofiler. It also causes your game to use more unique font sizes for the same glyphs, causing more problems for memory usage.

There are times when TextScaled is necessary, like scaled BillboardGuis. It’s more important to reduce usage than to try to completely eliminate it.

Avoid tweening TextSize

Constantly tweening text sizes for animations are potentially game-breaking. As mentioned above, this contributes to both overflowing the font atlas (due to a unique font size being used each frame) and frequent re-layouts per frame.

Avoid using too many variations of Fonts and TextSizes

Both design-wise and engineering-wise, it is beneficial to have limited variations of text style (font, text size) across the board. It will make your UI design cleaner as well as make good use of the available texture space.

This has become a bigger problem as we’ve added support for more fonts. It’s best to pick a few that fit your project, and stick to them.

Use MaxVisibleGraphemes for typewriter animations

A lot of existing tutorials and example code for typewriter animations have you constantly change the Text property using string.sub each frame. This forces a relayout of the text, and can cause problems with localization tools.

We recently shipped the MaxVisibleGraphemes property, which is a more performant way to do this.


We hope these tips help with your text performance!

111 Likes

A bit of clarification would be great here - for our studio’s game, I’ve implemented a typewriter effect by using a cache of individual TextLabel objects, one per every character (other than spaces). I do this rather than using the MaxVisibleGraphemes property because there’s a subtle, yet impactful extra detail I’ve added which tweens the whole TextLabel object downwards to the position it should be.

How bad would this be for performance? As stated I’ve a TextLabel pool that have preset properties which I re-use, but I’m still unsure in terms of performance how bad it would be.

6 Likes

Isn’t it also expensive to use AutomaticSize? What is more expensive overall? TextScaled or AutomaticSize?

In this case, the overall extra cost will be the text label instance sizes, which is roughly 2kb per instance. While this would cause huge issue if you are using features like AutoLocalize as all of those individual characters will be considered as translatable string. (Unless you manually apply translations over.)

4 Likes

The calculation for automatic sizing textlabel is similar to set size of it to fit the textbound of current font size once. While for TextScaled, it would try several sizes (including get glyph individual sizes, calculate kerning and typesetting) before it finalize and pick an ideal one, which would normally be ~10 (log(MaxTextSize)) times of repetitive work.

6 Likes

Text is my game is constantly flickering. I have no text animations. Text size is not being tweeend. I am only using 2 fonts. I have encountered this issue on all of my devices. (iPhone 8+,iPhone 14 pro max, iPad Pro, MacBook Pro)

Game: Mega Smash - Roblox

2 Likes

This issue was fixed after I reduced pixels/studs on my SurfaceGuis

I’m having the same issues… I’ve seen this happen with several of my other games before, and seems to happen on every mobile device (or almost) at a specific camera angle only. When your camera is at that specific angle, all text UI including on-screen, billboard, and surface UIs… It’s a big issue cuz in my game you can’t even see the text it glitches so bad… Changing pixel size might help a bit, but can’t even change the pixel size for the screen and billboard UIs, so not sure its a good fix :confused:

2 Likes

Hi developers, we recently enabled a change that allows a bit more memory space for text and it would be helping with flickering. We are working on further apis that allows you to manage SurfaceGui visibility according to distance so that you can occlude some of them if they are needed.

Meanwhile, you can check about how many layouts are performing within one frame and how many items are submitted for rendering. (You can bring up this by pressing crtl + f2 on desktop client or shift + ctrl+ f2 in studio.) It would help you identify overused text memory on 3d surfaces/billboards if you see high numbers on GlyphItems along with flickering in certain directions.
image

8 Likes

I know it may be a bit unrelated… but when will the UIBlur instance become a thing? It’s already done with the CoreGui, so I am surprised why it isn’t frontend yet.

4 Likes

This looks really useful! What does each item in UI DrawItems measure? Is Glyph Items the number of characters being displayed on a GUI? What things affect the value of Glyph Items? I’d like to understand how to use the tool, might help me optimize our studio’s game. :slightly_smiling_face:

Generally what is considered a high number for any of these statistics? My game is relatively UI heavy and uses 1816 glyph items, according to the ctrl F2 menu. We’ve noticed high memory usage and I’ve narrowed it down to UI, so I’m going around optimizing everywhere I can.

Is there a table or reference for the memory usage of each UI object?