Confusing RenderEngine deletion behavior

Within regards to custom render engines, I haven’t seen any documentation about the behavior of when Blender decides to call the RenderEngine’s __del__() function.

It’s seemingly sporadic, currently when I move the camera around the viewport, after about 3 seconds or so engine just deletes itself, leaving everything to just halt midway through interactive rendering sessions, obviously this can be bypassed by just not deleting your session when blender calls __del__() but surely there is a better way.

It seems updating materials and a slew of other things cause engine to get deleted by blender, I’d like to better understand why this is happening, and best practices to keep in mind when wrapping a custom render engine around this API behavior.

One confusing thing is that Blender never calls __del__ itself. It’s a method on Python objects that gets called by the Python interpreter whenever an object is about to be destroyed. This usually happens when the last reference to that object was removed, for which Blender is partly responsible. For example, if you store a reference to your RenderEngine object in some global scope that Blender doesn’t know about then the object will stay alive and __del__ will not get called at all.

But your second paragraph sounds like something fishy is going on. The RenderEngine should not get removed when interactive render is active. Nor should updating a material cause the current RenderEngine instance to get destroyed (and possibly a new one to get allocated). I believe it should only get destroyed when switching out of interactive render mode (or a final render is done), plus sometimes for certain undo operations. However, it seems one or two extra RenderEngine instances sometimes get created without being actually used. Maybe you’re seeing the destruction of those?

As far as I know, it happens in these cases:

  • Final render done
  • Viewport render cancelled by user, by changing shading mode to something else
  • Material preview render done

I have not observed such behaviour yet. Are you sure that you are tracking the __del__() of the correct RenderEngine instance? If you have the material tab of the properties panel open, it could be that RenderEngine instances are created for the material preview update, which are deleted afterwards. (In fact, there would be two instances, one for the large preview and one for the small thumbnail for the material selection list).
Obviously, your session should be an instance member and not a class member, because multiple RenderSessions can exist at any time.

I don’t technically use session but a object like session is set to the new instance of engine, however I do use that instance as a global variable which gets called to stop the engine and then that globally instanced variable gets deleted upon the __del__() function getting called, perhaps it is an issue with the global variable, I do however have preview renders turned off for now.

It also seems that updating a material deletes engine, I will verify this behavior again, but last time I checked updating a material would cause engine to delete itself, I believe luxrender makes note of that behavior as well.

The organization is as follows:

def create():
     engine.AI = IPR(engine, data, _dimensions, session=session, state=state)
     global AI
     AI = engine.AI
     global ARS
     ARS = engine.session

# This is called by __del__()
def free(engine):
    """Free any memory that was allocated to Arnold Render Engine
    This function is called when an ArnoldRenderEngine instance is deleted.
        Deletion triggers include:
        1) Updates to Material Properties or ShaderNodeTree
    """
    global AI, ARS
    try:
        if AI:
            AI.stop()
            AI = None
        if ARS:
            ARS = None
    except NameError as err:
        IO.error("AiModule undefined", AiLog=False)
 

Then this is most likely your problem. You can’t have only one global session because RenderEngine instances can be created by Blender at any time and as many as needed, even in unexpected places. For example, when evaluating the compositor, a RenderEngine instance is created as well just for the purpose of calling its update_render_passes() method.

I fixed it, I had a self.engine = engine hiding in my opengl drawing module :slight_smile: