Unity: Reporting UI progress properly

UnityEditorUtility.DisplayCancableProgressBar can be used to report progress of an operation, but updating the progressbar itself comes with a performance overhead and can affect the performance of your application.

In this post, I’d like to share how I prefer to update a progressbar to minimize the time spent on the UI. It’s so simple, that I was even uncertain if it’s worth a post, but since I see every now and then Unity extensions that report progress inefficiently imo, I decided to put it online. It’s also related to my previous posts (1 2 3), where I describe what I changed to make my Unity plugins perform faster.

Let’s assume we have x tasks we need to complete and want to report the progress. The simplest, but also naive way to do this is something like this:

Unity EditorUtility.DisplayCancableProgressBar

If calling DoWorkOfOneTask() is particularly expensive, the overhead of updating the progressbar every loop-iteration is to neglect. However, if DoWorkOfOneTask() is fast, you most likely end up spending more time updating the UI than “doing the work”.

Let’s take the following editor script example, which you can copy&paste and run in Unity yourself:

Both methods basically do the same, except one reports the progress while the other does not.

Executing NoProgressbar() takes about 1 millisecond to complete, whereas WithProgressbar() requires more than 10 seconds. These 10 seconds are spent just on updating the progressbar UI. It’s sort of artificially made slower due to the progressbar.

It’s often not known in advance how long a certain operation takes to complete. For example, if DoWorkOfOneTask() uses some sort of caching, the first few calls might be slow, but subsequent calls could be much faster. So it does make sense to display progress when the operation takes a bit longer, but makes no sense when it would be finished instantly.

What I find useful is to update the UI in timed intervals, only after a certain amount of time elapsed. This ensures fast operations stay fast, because there is either no or at least not much overhead of updating the UI and for slow operations, updating the UI isn’t an issue anyway.

Here is my personal choice how I like to report progress in my applications:

The code above displays a progressbar only, when the total time of operations takes longer than 1 second and then updates the UI in 100 ms intervals at most. This assumes each of the DoWorkOfOneTask() calls isn’t particulary expensive. If such call takes eg 1 minute to complete, the progressbar wouldn’t be displayed until the first DoWorkOfOneTask() completed.

Reporting progress like this works like a charm in my applications, such as Texture Overview and AudioClip Explorer. If their cache is up-to-date, starting the plugin is so fast that no progressbar is necessary at all. However, if a few cache entries are outdated and updating them takes a while, the progressbar appears after 1 sec and gets updated periodically until it completes.