Only loading fonts once in a plugin

It is my understanding that juce::AudioProcessorEditor gets destroyed every time the user closes the UI (in Ableton Live at least, but maybe in all DAW’s?), does that mean we can’t (or shouldn’t) keep things like fonts in memory until the user opens the UI again?

I’m looking into possible performance improvements for my UI, and loading typefaces (via juce::Typeface::createSystemTypefaceFor) can take quite a bit of time depending on how many fonts are involved. It would be nice to keep them in memory so the UI doesn’t lag behind when the user opens it.

I tried keeping the fonts in static memory, but obviously this is probably not a great idea. (Plus Juce assertions about memory leakage are thrown.)

Any thoughts and tips would be appreciated!

IIRC, createSystemTypefaceFor() does literally what it says, and ‘installs’ an embedded font to the system. That means that after calling that, you can get that system font back again like you would for any other font:

createSystemTypefaceFor (/*MyCustomFont.otf*/);

// ... later

juce::Font font {"My Custom Font"};

So, you could do a check for if “My Custom Font” is installed to the system when your UI opens, and only then create it if it doesn’t already exist?

Not 100% that’s how it works, but I recall doing something similar to this in the past…

It looks like the actual refcounted object returned is (on Windows) a WindowsTypeface, which has a destructor that calls RemoveFontMemResourceEx, effectively unloading it from “system” memory.

So that means the fonts are still unloaded when every reference to the typeface is removed.

Ahh okay, that does make sense.

I don’t see any harm in creating the typefaces with the creation of each instance of your plugin, and just keeping those references around, even while the UI isn’t open.

My plugins usually had 4 typefaces - a body font and a title font, and a light and bold variant for each and I never experienced any issues with performance when creating those typefaces.

How many typefaces are you creating?

Yeah, that’s what I’m thinking - but what would be the best way to do this w/o tripping up the memory leak detector? Keep the references in the plugin processor? That kinda feels like the wrong place to me.

The loading time isn’t too bad actually (about the same amount as you + an additional icon font), but if it’s something I can avoid to make loading the UI just a little more responsive, then I definitely will.

I have a Fonts.h with some getters for each font, then in Fonts.cpp I just have some global static constants:

static const juce::Font myFont {createSystemTypefaceFor (...)};

juce::Font getMyFont() { return myFont; }

So they’ll be created once at launch, then hang around until the instance is destroyed.

Not sure if that’s necessarily recommended, but it works.

Yeah, that’s what I was trying, but it’s tripping the LeakedObjectDetector<WindowsTypeface>::LeakCounter assertion that there’s leaked memory. Seems related to the order in which static instances are destructed, my static Typeface::Ptr objects being destructed after the assertion hit.

Only difference I can see there is that you’re holding the pointer, rather than a juce::Font as I do. Not entirely sure how either of those works off the top of my head, but I do know we don’t have any issues with memory leaks doing it this way!

You can try if putting your fonts into a struct that inherits DeletedAtShutdown helps. That way it is destroyed orderly when the ScopedGuiInitialiser goes out of scope (somewhere in the wrapper).

1 Like

This seems to work perfectly! Had no idea this class exists, thank you :slight_smile:

1 Like

You can also use a SharedResourcePointer

Rail

Almost, but that faces the same pitfalls as that is reference counted. You’d still need a way to keep it in memory when the editor is closed (when the editor does not exist). A good old singleton with juce::DeletedAtShutdown works great for this.

The thread heading says “once”… There’s only one AudioProcessor so the font is a SharedResourcePointer member of the AudioProcessor… and if there are multiple instances in the same process space they’ll share the same font (or graphics if you have a member SharedResourcePointer of a SharedGraphics class object).

Rail

2 Likes