Progress Dialog for Slow Blocking Operations [Proposal]

When an operator, file loading or the dependency graph take a long time, Blender currently just hangs and will appear as if it’s crashing even if it isn’t going to. This looks bad and is not helpful to the user in any way.

Ideally we should make Blender’s UI should always interactive to some degree by moving the computations to a separate worker thread. However, making our existing drawing code work while operators are modifying data or the depsgraph is not fully evaluated seems impractical currently.

This document proposes an alternative approach that is more useful than not doing anything about it and should be more straightforward to implement.

Proposal

The proposal is to have a progress dialog like in the following video. Note the UI is obviously very work-in-progress but functionality wise it is working already (slightly adapted from this PR).

This dialog should automatically pop up after Blender is blocked for a couple of seconds. The exact time needs to be chosen so that it’s not too short that it pops up unnecessarily and not too long so that Blender does not appear like it’s crashing.

Information in Dialog

The dialog itself can contain some useful information like:

  • Processing time: Showing the time the current operation is running already makes it obvious that Blender is not yet crashed. Also if people build operators or node groups which take a long but consistent amount of time, this is almost like a progress bar. While Blender does not know how long the operation will take, the user might know.
  • Status message: We could show status messages like Evaluating: <object name>, <modifier name>.
  • RAM and CPU usage: This can help the user to make an educated guess whether they should attempt to kill the process before it e.g. uses up all RAM slowing the entire system down. Not sure if we can easily access this information on all platforms, but it would be good to include on the platforms where we can access this info from the OS.

Recovery

Limited forms of interactivity can be supported in the dialog as well. Buttons and shortcuts can work to some degree. It’s not entirely clear if we can reuse much of our existing ui code for this though.

A “Close Blender” button would be especially useful. It essentially kills Blender when there is no hope that Blender will finish the computation, either because it would run out of memory or because it would take an eternity.

For extra usefulness, we could also attempt to perform an undo step and then save a recovery file. This might not work in all cases reliably, especially if the blocking operation is modifying DNA data. However, it probably succeeds in a large number of cases and avoids losing work. The undo step is necessary because otherwise one would usually have the same blocking operation when attempting to open the recovery file again. I’ve tested this recovery mechanism before and it appears to be working surprisingly well in many cases.

It’s probably a bit out of scope for the initial implementation, but ideally we’d also have a “Cancel” button that attempts to stop the current operation. I say it’s out of scope, because cancelling arbitrary computations is very hard. Also it’s not obvious what should happen when cancelling a depsgraph evaluation, because we need an evaluated depsgraph in the end regardless. For operators, this might be a bit simpler to implement, but can also be done separately.

Implementation

To achieve all of this, we still have to move the work off the main thread. That’s necessary because the main thread is the only one that can reliably get events from the OS currently. That was the limiting factor in my initial attempt at solving this.

The key non-obvious aspect is that while we use a separate worker thread, the main thread will still be blocked semantically, e.g. it does not proceed to redraw the 3D view while the depsgraph is still evaluating. Instead, the main thread is running a mini event loop and just draws on top of the previously rendered Blender UI.

This patch can be used as reference for the implementation.


If we decide to implement this dialog, it would be very useful to have some good mockups for how it should look like.

19 Likes

I guess you would regard this as out of scope too, like a Cancel button, but the thing that would likely be most useful to users is a progress bar. But I admit it is not easy to think of an API into a computation that would allow getting progress updates, and that’s the easier part. The hard part for many computations would be: is it even possible to estimate “percent done”? This gets especially tricky when our modern computations are heavily parallelized. I could think of have the parallelized threads increment some atomic counters that could be consulted to judge progress, but would be worried about the potential performance impact of that.

Without have at least one button to push (Exit Blender, Cancel, …), or the undo step/recovery save mechanism, or a progress bar, it is hard for me to understand the added value of a screen showing that “I am doing something” vs just looking at the static screen: they both kind of mean the same thing to me, in the absence of some assurance that “something” is not just, say, infinite looping.

P.S.
On a Mac, if “Performance Debugging” is checked in the XCode properties of a binary (it is by default, for Debug builds), then I have noticed error reports that I believe are about priority inversions caused by heavy computation on the main thread. If the result of this proposal is only to move such computations off of the main thread and stop those error reports, I’d regard that as a good thing.

5 Likes

Yeah, a progress bar is out of scope for me right now. It doesn’t seem possible to do that reliably for more context scenes. I rather have a simple timer than a misleading progress bar. As mentioned in the PR, I think often the user will know how long something will take approximately, because often anoperation is done more than once. So in that case, a timer is good enough.

For some operations, the updating status messages can also be similar to a progress bar, but without guarantees about the ETA.

The exit button or some other way to close Blender is in scope for me. That should be fairly straight forward. Depending on what people think about the recovery file approach, that’s in scope for me too.

I don’t know about MacOS, but for me there is a big difference, because Blender keeps processing events instead of just being blocked. That also means that the OS will not ask me if I want to close Blender after 5s or so, which can be quite annoying when something takes e.g. 10s. So even with just the timer, it would improve the user experience quite a lot imo.


Obviously, a proper progress bar is always better, but I’m really only talking about the last line of defense here.

I guess before we commit to a workaround like is described in this post, I’d be curious to see an overview of the sort of problem’s we’d have to fix or tradeoffs we’d have to make to allow drawing with a partially evaluated depsgraph, since there’s general agreement about that being the ideal solution. I think there’s also agreement that it would be hard, but I’m not sure we know how hard unless someone does some thinking about how to do it. Personally I would be much more motivated to work towards a more ideal solution like that compared to a workaround, even if it took longer.

4 Likes

Progress and cancel might be out of scope for the initial implementation. But once this mechanism is in place, I think it would be relatively straightforward to it add as something operators can opt into.

The way I imagine drawing to work is that it draws the cached GPU buffers for each region, and draws a popup menu region on top. Same as regular drawing, just that you’d postpone redrawing editors.

Operators that modify the UI (windows, screen, editors), use the GPU API (e.g. selection) and load or save blend files would need to remain blocking, or at least in part.

But beyond that I think the UI drawing code can be made to work? There is various CTX_data access in the UI code, most of which you wouldn’t hit because it’s for things like animation or color management. But I think we could have a context without data pointers and add appropriate checks in the UI code.

For drawing with a partially evaluated depsgraph, are we talking about doing that after cancelling (or at least pausing) depsgraph evaluation? Doing it in parallel seems impractical to me, but keeping Blender somewhat functional after cancelling or pausing depsgraph evaluation might be possible.

3 Likes

I prefer this ideology I think.

But if I’m to comment on the video demonstration as is, I’m not a huge fan of how intrusive the window is. It covers the geometry nodes graph, and often when blender is computing and I can’t do anything, I like to ruminate over the graph and sometimes get better ideas. If it’s blocked I can’t always do that. Also if I lose vision on the 3D viewport it’s a bit frustrating to not see the result I currently have to ponder what else I should do.

I suppose it does very clearly communicate though that blender is in fact not crashing, and I do like the idea of the dialogue giving some helpful information, like which nodes are taking the longest. I’m just not sure if it’s worth losing vision over most of your setup.

I sort of prefer a small UI element at the bottom of the screen like this:

6 Likes

Unfortunately, there is way more to the “ideal” solution than just drawing with a partially evaluated depsgraph. First we’d have to be able to cancel any operation safely (if we can’t, then we still need a fallback like described here). Then we actually need to define what drawing with a partially evaluated depsgraph means UI wise. Then actually implementing that is still quite involved probably. Also, that only covers the case of partial depsgraph evaluation and ignores the case when operators or file loading is taking a long time.

Cancelling operations is quite tricky, because to make it reliable in a way that we don’t need a fallback, we basically need to make all loops that could take a long time cancellable. There are many such loops, including many that we might not even have control over. I’m not sure if e.g. opensubdiv and openvdb provide ways to cancel operations. If the code that we do control is hard to make cancel-safe in all cases.

A more aggressive approach would be to kill the worker threads but that’s very bad practice for good reason. I basically have no idea what state the worker threads were in when they are killed, leading to undefined behavior and leaks. Not to mention that this would also kill the threads from the TBB thread pool which TBB probably doesn’t like. An alternative that other software (like browsers) use is to run stuff in separate processes. An entire process can be killed more safely. However, that requires quite a different design from what we have in Blender.

All that is to say, I think we really need this reliable fallback solution that works in any case. Cancellation of some specific tasks can be implemented on top of that.

Yup.

Yes. In my patch I did the first part already which worked nicely (only annoying thing is when the window is resized, but we can probably ignore that for now). I didn’t attempt drawing a popup menu on top. Probably requires some cleanup in some cases but I could see that working too.

I’m less sure about how difficult the event handling is when the popup has buttons.

Yes, the drawing in my demo is obviously quite bad. Thanks for the suggestion. I agree that it should be less intrusive. Personally, I would not put it in the status bar for the simple reason that some people have it hidden and I think this dialog should not be hidden in that case. Using a normal popup menu region like Brecht suggested, that ideally can also be moved around seems best.

1 Like

That sounds reasonable, I agree.

As in being able to be moved around during the computation, or it will just remember it’s last location? I’m on an old cpu, Ryzen 7 2700X (deep sadness), and I find if blender stalls during computation there is no way to interact with anything. It’s frozen until it’s done. I always wondered if there was a way to leave a little bit of leeway to still allow navigation in the geometry nodes tree.

  1. Cancel Blender button shouldn’t appear with the dialogue. For me it would imply “you might as well click it”. Window appearing that immediately gives you an option to close suggests to me that it’s time to close, not that it’s a potential fix for a potential problem. Button should appear after some time, after dialogue is already there, maybe with a message like “it’s taking longer than expected, want to close?”

  2. Would it be possible to have debug prints in the window if Developer Extras are active? I’m thinking case where my python operator does loops and prints stuff for each loop. That information might help me decide whether I want to close Blender or not.

2 Likes

I agree with having something in the bottom bar rather than a pop up
that’s already where rendering progress is shown

I also like the Houdini approach of showing which node is currently being processed in the graph in the same bottom bar

5 Likes

Independent of this specific use case, partial depsgraph evaluation would be great for exporters; it’s a lot easier to export instances when you’re working from the inputs, rather than the outputs, and curried/incremental evaluation is generally the way to go for that.

Implementing partial evaluation without user-facing tools for subgraph isolation can be a bit futile, though, in my experience. It’s easy for users to build scenes where you can’t reliably perform heuristic edge cuts, which leads to scenarios where they don’t really benefit from partial eval due to unbalanced partitioning.

It’s hard to make it useful without making folks use it explicitly, and that comes with a lot of design work.

1 Like

In my experience the message dialogue needs to be large and intrusive.

The reason for this is that while blender stalls you will often want to do something on your other monitor but get notified as soon as Blender snaps back.

If the message dialogue is too small you will not notice.

At the moment, while there is no message dialogue there is a trick to gray out Blender when it stalls. You simply have to click the Blender window a couple of times and it will turn gray. I think windows is responsible for this. As soon as Blender does not stall anymore it is ungrayed.

I use this when importing large files for example which can take anywhere from 20 seconds to several minutes or longer. Or when running scripts that take a long time to execute.
Whenever i do not gray out Blender i do not recognize when it snaps back to life and I waste time doing secondary work on my second screen.

Please make the dialogue a large as possible or create some kind of visual fog horn event that notifies the user when continuing work is possible.

2 Likes

A large window informing you about the operation being performed is certainly useful for this, but won’t it stand out from the style of the entire interface? I think that eventually a solution will be found that satisfies all the parameters.

Faced with the same types of issues (unresponsive whole UI since there’s some heavy operation that happens on the same thread as the UI; sometimes even outside of main apps control – e.g. imagine any arbitrary addon code), I was part of effort implementing a similar “automatically popping up Busy Dialog” at my previous work (Unity game engine).

Once that shipped to the masses, there was one aspect that was surprising and caught us off-guard: because various UI stalls now did bring up “hey I’m busy for N seconds!” dialog, many people perceived this new version as being much slower than any of the previous versions. This can’t fully logically explained, and in each and every case I’ve seen, once you sit down with someone having this complaint and dig in, you realize that the previous versions had exactly the same stalls in exactly the same places. But the perception across the many, many users was still that “ugh, this version keeps on getting stuck way more often”. Perception, “impression” and “feeling” are powerful things, turns out!

That said, there have been people, scenarios and situations where this “busy” popup was useful. Here’s a random sampling of points where it was:

  • We had a “busy for X” timer. First it would go “X seconds”, if it becomes longer than two minutes it switched to “Xm Y seconds”, if it becomes longer than an hour it shows “Xh Ym Z seconds”. This was useful since it gave people immediate access to the duration of the stall; instead of “feels like this takes at least a minute” now they can immediately say “definitely takes at least a minute”.
  • Others above suggested actual “percentage progress”, but my impression is that it is impossible. The whole point of this dialog, is that it shows up in places that arguably should have had a proper progress bar, but they do not, since the author of the code did not expect or realize it would be taking a long time (or simply forgot / did not care about a progress bar). From this it follows that you could treat each occurence of this “busy” indicator as a defect - “this operation should report progress in some way”.
  • The default amount of time when this busy dialog would appear was configurable in user preferences; and IIRC default was 10 seconds.
  • In addition to the time, we also had indicator of roughly which “module” the code is busy right now. In Unity’s case it was simply the currently executing C# assembly (if within C# code), or one of topmost entries of native callstack. The busy dialog display code was periodically sampling the main thread callstack and displaying information based on that. A trick: for native code callstack, it is often better user indication to not display the topmost function, but rather something like 3rd or 4th stack frame; the top is often not descriptive enough (like “busy doing memcpy” – gee thanks :)). I think we also had ability for Ctrl+C key and/or button on the dialog to copy full busy callstack into clipboard.
  • Each time the busy stall dialog was popping up, we also saved something like “stalls.json” file in the temporary location of the project, in Chrome Tracing format. In Blender’s case there is no “project folder” concept with known “temporary files” location however. This one is more of a “technical enough user” feature though; one could argue that if they are that invested, they could just as well attached a profiler to the process and actually see for themselves where it is being busy.

Anyway, my main point of something to watch out, is possible perception from users that “now blender is much slower”, which again, is not based on any actual data, just this new dialog makes them perceive things as being slower.

19 Likes

That’s not possible unfortunately. It’s kind of the entire problem that we can’t redraw the normal UI while there is a blocking operation.

Yeah that seems reasonable. Maybe there should be an exception when we can detect that the RAM usage goes up very quickly.

I think we should use a proper API for these kinds of progress messages. Other than that, this seems possible.

Makes me think of this classic xkcd. :smiley:
That’s also a good point though. Maybe out of scope initially, but I could imagine there being some “Notify me when done” button.

That’s a very interesting point indeed. Since this is just a perception issue I feel like this can be solved with better design. For example, maybe slowly fading the dialog in (in some way or another) results in less of a distinct “now Blender is slow again” moment.

This would be a massive help for me.

Often, when a task is taking a while to complete, I’ll do other things on my computer to pass the time; having the option of receiving OS notification when Blender is responsive again would be much preferable to having to alt tab every so often to check if the process is done.

Two thoughts from a UI design perspective:

I would advise against showing an increasing timer (the ‘processing time’). It’s not good psychology, because time goes slower when staring at the clock. I don’t think you can find any UI design handbook recommending using one. The general idea is: don’t emphasize the negative in such dialogs.
I guess the experience Aras is describing, confirms that.

So use a spinner when you can’t estimate duration and use a progress bar when you can. Along with the other things you mention: status messages, ram/cpu usage etc.

And a nuance about progress bars: people are used to speed variations in them, they are not tapping the beat to check if they are entirely accurate. Half of the progress bar is psychology too: knowing the order of magnitude of the action, knowing there is an end to it, seeing Blender doing its very best to reach the finish line.
Of course, when you can’t promise anything, don’t use them. But as a developer, don’t think too strict about them, because they tell more than just the percentage. And it’s easy to program them a bit conservatively, slower in the beginning, faster to the end. Users perceive the faster part as a piece of luck.

12 Likes

If it’s going to be a big popup, having a high default time like 10 or more seconds like this would be really nice. The 3 seconds in the demo seems very short. Before that triggers, perhaps an indeterminate progress bar could communicate that things are processing without being so intrusive? #111096 - UI: Indeterminate Progress Indicators - blender - Blender Projects

1 Like

For OpenVDB at least i know that many expensive operations have an “interrupter” argument that can be used to cancel an operation.
https://www.openvdb.org/documentation/doxygen/structopenvdb_1_1v12__0_1_1util_1_1NullInterrupter.html#details

1 Like