One long-standing issue with Blender is that when the user accidentally starts a long-running operation, there is no way to stop it without force-closing Blender. We support cancellation in a few places already, most importantly rendering and baking. However, for everything else we don't really have a system to make that work. Typically, one would try to split up the ui thread from the processing thread to keep the ui responsive. Unfortunately, this approach is very hard to use in Blender, because generally all ui code expects everything to be evaluated. Data corruption would be very likely, even more so when some Python operator is running while we want to redraw the ui.
Fortunately, there seems to be another approach, which is not as good but solves the biggest problems as well: Instead of separating the ui from processing thread, we can try to separate the user-input from the blender-main thread. This would allow us to retrieve events from the user while Blender appears frozen. The user-input thread could request task-cancellation from the processing threads via a global variable.
This patch is a prototype that implements this separation using a new ghost event consumer that runs on a separate thread. It gets all the events even when the main thread is still doing something else. I already found that the current implementation does not work on windows. The reason seems to be that only the thread that created the window can also retrieve the events. So a better solution seems to be to move more of the ghost interaction to a separate thread, which still seems doable with a bit more work.
Overall, given that this patch already works in linux, it feels very achievable to get it working on other platforms as well. I think the benefits of having cancelable operations is huge, even if we don't have the even-better ui thread separation.
To test the patch (on linux) do the following:
- Create a new script in Blender like the one below.
- Start it and note that Blender is frozen.
- Hit shift+esc to stop the script.
- Blender is now in a "cancelled" state which allows the user to "fix" the scene.
- Click the "Enable Processing" button in the topbar to make Blender behave like usual again.
import bpy
while True:
if bpy.context.cancel_requested:
breakSome feedback on the general approach would be appreciated. Maybe someone also has a different/better idea for how to get that working?
Discussion Points:
- How to retrieve events from the OS in a separate thread in a way that works on every platform?
- What to do when an operation has been cancelled? Operators should just be undone, but cancelling the depsgraph is harder because Blender expects there to be an evaluated depsgraph for drawing.
