Font::getStringWidth on non-message thread


#1

I encountered a crash in WindowsTypeface::getOutlineForGlyph when a text is drawn and at the same time another thread is querying a string width using Font::getStringWidth.

This is using an older version of JUCE (3.0.5); I’m not sure whether this is relevant for this issue, the according code in juce_win32_fonts.cpp doesn’t seem to have changed much. I’m also rather sure that I only experienced the crash on Windows.

From what I see, the crash is due to some stuff in Font::getStringWidth that isn’t thread save and shouldn’t be called from threads other than the Message thread.

Of course, one fix would be to move the string width query on the message thread but maybe there is another thread-save solution available to get the width of a string for a certain font?


#2

I moved all calls of Font::getStringWidth into the message thread which fixed the crashes.

To avoid this kind of crash it might be useful to add the following line to Font::getStringWidthFloat:

jassert(MessageManager::getInstanceWithoutCreating() == nullptr || MessageManager::getInstanceWithoutCreating()->isThisTheMessageThread());

Not completely sure whether Font::getStringWidthFloat should be called without a message manager in the first place. Depending on that, the “== nullptr ||” part should be changed to “!= nullptr &&”.


#3

Thanks - good call. TBH I’m surprised it didn’t work, as a lot of the font stuff should be threadsafe, but I’ll certainly add this assertion until we can guarantee that.


#4

BTW font rendering and thread-safety i remember this problem, where i already posted an example how to recreate a crash


#5

Would have been a good time to check this out:

Rail


#6

Are there any efforts to make the whole Graphics Code (including font rendering) thread safe, i render images in a background thread (to have less consumption on message-thread) which works really great, but it would be cool to also use it with font rendering.


#7

totally agree with @chkn


#8

bump


#9

ACK

Am a bit surprised that it would have any problems, I thought it was mostly thread-safe, but maybe there’s an unprotected bit of code in there somewhere.


#10

Just adding a some kind extra read-write lock? So we can get rid of the jassert’s?


#11

Is there any time-schedule when these potential threading issues will be sorted out.
Im just talking about calling functions on a graphics object serially, on a background thread.

void run()
{
  Graphics g;
  g.doingthis();
  g.doingthat();
}

(This opens the door to very cool performance optimisations)


#12

I’m certainly not very knowledgable in the depths of how Graphics works but I think the problem is that font rendering is done using some OS methods which aren’t thread safe. It might be hard to fix this without reworking the whole font rendering on Windows.

Some years ago (juce 1.5 times) I also tried rendering images on another thread and simply drawing the image in paint but in my experience this didn’t gain much performance. It might depend on the application but for example drawing a path and some grid for an analyser might be faster than drawing a full image, especially when the gui size increases.
Of course this was some time ago and my approach might have been a bad one in the first place.


#13

If you have very complex graphics, you better compose them on a background thread, to prevent blocking the message-thread.
I’m already doing this very successfully, and it works like a charm on both platforms.

So i was surprised when the assert in Font::getStringWidthFloat() was implemented, because i didn’t have any issues (with Directwrite turned on)


#14

Out of interest, have you compared offloading to a background thread with using OpenGL?

We have DirectWrite disabled because we experienced crashes in some hosts (don’t remember right now which ones) and haven’t tried turning it on again.
So maybe DirectWrite is already threadsafe which could be taken into account for the assertion.

I might add that the crashes we experienced due to Font::getStringWidth() were very rare and only appeared if it was called extremly often.


#15

I experimented to use the OpenGL Renderer, but this doesn’t improve anything, because if you have 2D vector graphics, they will be just rasterised to lines (CPU) and these are drawn by OpenGL. (I guess) even CoreGraphics uses for the most operations the CPU instead of GPU, because they can much quicker calculated on a CPU.

Also OpenGL was much to unstable/unreliable for the use with plugins.


#16

I would like to see if getStringWidth() could be used on a non message thread. (bump)


#17

Sorry for my impatience, but will this problem solved in the near future?


#18

Sorry, had dropped off our radar. I’ll try to take a look soon but no promises!


#19

If this once be fixed, this could be the begin of whole new era.
What about a repaint((Graphics &g)[]{…}), function which takes a lambda as argument.
The paint, is performed on a background thread and buffered,without blocking the message thread, and than displayd once its ready.
This would be very interesting to visualize audio data.


#20

It would be interesting to know if the creator of this thread can recreate the problem with Direct X font rendering on (default) and the latest juce.