I'm trying to render a bunch of actions with a modal operator that is called with a timer. The first action is rendered as expected, and strangely some times the second and third are rendered aswell, but at some point
bpy.ops.render.render("INVOKE_DEFAULT", write_still=True, animation=True)
returns {'CANCELLED'}, which breaks my callback handler. If i render it in serial without "INVOKE_DEFAULT" it works just fine. I also tried calling the render operator multiple times if it failes, but without success.
If I use a higher timeout like around 2 seconds it seems to work just. But why? Is there some kind of race condition between my modal operator and the render operator?
class BC_render_modal(bpy.types.Operator):
"""Start blenderclonk render process"""
bl_idname = "action.render_modal"
bl_label = "Start render"
_timer = None
rendering = False
render_lsit = []
pending_list = [] # Actions of the currently rendered mode
composition_list = []
@classmethod
def poll(cls, context):
return True
def execute(self, context):
wm = context.window_manager
# Prepare render list
self.render_list = create_render_list(context)
# Register timer to check render status
self._timer = wm.event_timer_add(0.1, window=context.window)
# Register post render reset function
bpy.app.handlers.render_complete.append(self.render_complete)
wm.modal_handler_add(self)
return {"RUNNING_MODAL"}
def render_complete(self, scene):
self.rendering = False
def modal(self, context, event):
if event.type == "TIMER":
if not self.rendering:
if self.pending_list:
print("Render action")
self.rendering = True
action_name = self.pending_list.pop(0)
anim.selectAction(action_name)
bpy.ops.render.render("INVOKE_DEFAULT", write_still=True, animation=True)
elif self.render_list: # Next render block
print("Prepare render")
mode, actions = self.render_list.pop(0)
self.pending_list = actions
prepare_materials(mode)
if mode not in self.composition_list:
self.composition_list.append(mode)
else:
wm = context.window_manager
wm.event_timer_remove(self._timer)
bpy.app.handlers.render_complete.remove(self.render_complete)
for mode in self.composition_list:
ComposeGraphics(mode)
self.report({"INFO"}, "Render finished successfully")
return {"FINISHED"}
return {"PASS_THROUGH"}
EDIT:
Seems to be an issue with the render thread reporting finished before the job is actually done. Check out https://developer.blender.org/T52258 for detailed explanation.
Workaround: This will lead to multiple failing attempts to start the render process but renders it eventually.
def modal(self, context, event):
if event.type == "TIMER":
if not self.rendering:
if self.pending_list:
print("Render action")
self.rendering = True
action_name = self.pending_list.pop(0)
anim.selectAction(action_name)
result = bpy.ops.render.render("INVOKE_DEFAULT", write_still=True, animation=True)
if result == {"CANCELLED"}:
print("Render failed!")
self.pending_list.insert(0, action_name)
self.rendering = False