How to render components on a background thread?

Hi JUCErs,

I have a Viewport with a grid of thumb components inside of it. Each thumb has a couple of images, some text and a few shapes drawn. Once rendered, those thumbs don’t need to change.

Right now, I call setBufferedToImage (true) inside the constructor of the Thumb component. And this improves scrolling of the Viewport significantly. But the Viewport doesn’t seem to render images that aren’t visible (which is reasonable), so on a first scroll the User Experience isn’t great. The moment everything has been shown at least once, scroll works without any freezes. But even if it draws everything, I don’t seem to find anything about virtual scrolling in JUCE, so the load of so many components freezes the UI (since it’s hogging the Message Thread) and the user would still experience a huge freeze of the UI.

I don’t want my users to ever to see their UI freezing. If there is something to be loaded - notification of loading is shown (notification pops or modal AlertWindow with a progress bar is shown), while in the background work gets done, but I want those sliders buttons and knobs to still be working without any jitter.

Since there is nothing dynamic about these thumbs, can I have them detached from the components tree (so there is no synchronization issue with the Message Thread accessing them), render them with some sort of a detached graphic context (I really don’t know if there is such a thing) and once ready,
signal the message thread to add them up with addAndMakeVisible()?

Any help is much appreciated.

Cheers.

Update:
I will try caching the images on a separate thread. Maybe this would solve the initial UI freeze problem. The first-scroll issue remains, though…

2 Likes

One of the biggest culprits for the jitter in the UI, were images that had a ridiculous resolution. They were too tiny as file size (<1MB) to suspect a 16000 x 10000 resolution, which uncompressed was a huge thing.

I further improved performance by creating components on a separate thread and editing the components tree with MessageManagerLock.

Images are cached, and so is component rendering (through setBufferedToImage()).

Finally, I made progress indication for what’s going on in the background and now everything is running nicely.

I still think scrolling isn’t smooth enough (even in Release build), but it doesn’t look like a load problem (it does run fast), seems like we are missing some “magic” for real scroll smoothness. :slight_smile:

But I still haven’t answered the original question of this topic - can we prerender components on a separate thread… It would be nice if someone from the JUCE team would comment.

1 Like

We do (in psuedo-juce):

// bg thread
ScopedLock lock(imageLock);
Image img;
Graphics g(img); 
paintEntireComponent(g); 

// fg thread
ScopedLock lock(imageLock);
drawImage(img);

Allowing for retina monitors and things makes it a bit more complicated. I don’t know if this is the best solution, but it moves everything other than splatting (technical term?) the image to the screen onto the background thread. Reflecting on it for a moment it could be improved by having two image buffers to avoid holding the lock for so long…

3 Likes

I’ve noticed this before occasionally - I have wondered, but never measured, whether the trick is to refresh the graphics on a timer and interpolate the mouse events…

Thanks Jim.

I didn’t know I could just create a Graphics context with an Image (somehow I’ve only seen the constructor at the bottom of the class accepting a low level context).

What is the reason high-dpi screens are not covered by this method?

Don’t know. I think Safari and Firefox had the best scrolling within the web browsers the last time I checked, so we could probably take a peek in the code of WebKit and Gecko and see how the magic happens. But it isn’t one of those things that just work if you don’t have performance issues in your app, it is a feature.

https://pavelfatin.com/scrolling-with-pleasure/

1 Like