Deciding when to run tasks directly or on the idle loop
The fsl.utils.platform.Platform.haveGui
property used to return True
only if the wx.MainLoop
was running. This was changed to cope with situations where FSLeyes is being executed from within an IPython or Jupyter notebook session, in which case the wx.MainLoop
is called periodically, so is not always running.
But in this scenario, we still need tasks to be executed asynchronously via fsl.utils.idle.idle
, which uses haveGui
to decide whether a task should be executed directly, or asynchronously.
However, this change has introduced problems with off-screen rendering. The off-screen renderer has been written to assume that all tasks posted to the idle
loop are executed synchronously. But during OpenGL initialisation, a wx.App
(and a hidden wx.Frame
and wx.glcanvas.GLCanvas
) is created in order to acquire a GL context1. Because of this, the idle
function can now incorrectly schedule tasks to be executed asynchronously, because its decision is solely based on whether a display is available and a wx.App
exists, but not whether a loop which can execute tasks is actually running.
I've worked around this in fsleyes.gl.__init__
, by clearing references to the temporary wx.App
(and GUI objects) which are created during GL context initialisation. This has the effect that the wx.App
is destroyed, and subsequent platform.haveGui
checks will return False
.
But this feels like a very flimsy solution, which I'm not at all happy about. I think that I may need to implement some global context in the idle
module, which allows me to override the sync/async decision. For example, my render initialisation logic could be something like:
import fsleyes.gl as gl
import fsl.utils.idle as idle
# initialise GL, allowing idle to
# decide how to execute tasks
gl.init()
# From this point on, idle must
# execute all tasks synchronously.
idle.alwaysSync()
...
Ugh. I'm getting a coffee.
1 This occurs for both on- and off-screen rendering. If the off-screen renderer were executed without a display, we could avoid having to do this by using OSMesa. But this is not currently an option because of our dependence on freeglut for text rendering, which does not work in headless environments.