開発のヒホ

iOSとかAndroidとかのアプリを開発するのに四苦八苦するブログ

PythonのmitsubaでRenderJobのエラーを得る

 RenderJobRenderQueue辺りを使って多数の画像を複数同時に作れるが、RenderJobはメモリエラーか何かで画像の生成に失敗する時があるらしい。しかしクラス内にエラー処理するリスナーが見当たらない。

 RenderJobMitsuba Renderer: mitsuba::RenderJob Class Reference

 あまり良くわかってないけどたぶんRenderQueue内のregisterListenerを使えということなのだろう。

 RenderQueueMitsuba Renderer: mitsuba::RenderQueue Class Reference

 RenderListenerというものがあるらしい。数十MBもあるDocumentationPython integration項目にこれを使ったサンプルはない。2015/09/12現在これがPython対応しているかどうか明記されてない気がするが、なんとかしてみる。

 RenderListenerMitsuba Renderer: mitsuba::RenderListener Class Reference

 まずRenderListenerの存在を確認する。

from mitsuba.core import *
from mitsuba.render import *

RenderListener
# <class 'mitsuba.render.RenderListener'>

 続いてRenderListenerのクラス内メソッドを確認する。
 参考: How do I get list of methods in a Python class? - Stack Overflow

inspect.getmembers(RenderListener, predicate=inspect.ismethod)
# [('__init__', <unbound method RenderListener.__init__>),
# ('__reduce__', <unbound method RenderListener.<unnamed Boost.Python function>>),
# ('finishJobEvent', <unbound method RenderListener.finishJobEvent>),
# ('refreshEvent', <unbound method RenderListener.refreshEvent>),
# ('workBeginEvent', <unbound method RenderListener.workBeginEvent>),
# ('workCanceledEvent', <unbound method RenderListener.workCanceledEvent>),
# ('workEndEvent', <unbound method RenderListener.workEndEvent>)]

 API Reference通りの実装がされていそうだ。とりあえずListenerを継承したクラスを作ってregisterしてみます。

from mitsuba.core import *
from mitsuba.render import *

queue = RenderQueue()

class TestListener(RenderListener):
  def __init__(self):
    return

testListener = TestListener()
queue.registerListener( testListener )

# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# Boost.Python.ArgumentError: Python argument types in
#     RenderQueue.registerListener(RenderQueue, TestListener)
# did not match C++ signature:
#     registerListener(N7mitsuba11RenderQueueE {lvalue}, PN7mitsuba14RenderListenerE)

 oh...なんだこのエラー・・・とりあえずダメ元で全クラスメソッドをそれっぽく実装してみます。

from mitsuba.core import *
from mitsuba.render import *

queue = RenderQueue()

class TestListener(RenderListener):
  def workBeginEvent (self, job, wu, worker):
    return
  def workEndEvent (self, job, wr, cancelled):
    return
  def workCanceledEvent (self, job, offset, size):
    return
  def refreshEvent (self, job):
    return
  def finishJobEvent (self, job, cancelled):
    return

testListener = TestListener()
queue.registerListener( testListener )

 おっとエラー出ない。素直な実装に感謝しつつ、適当にprint文放り込んでRenderJobを動かしてみます。

import multiprocessing
from mitsuba.core import *
from mitsuba.render import *

# Start up the scheduling system with one worker per local core
scheduler = Scheduler.getInstance()
for i in range(0, multiprocessing.cpu_count()):
  scheduler.registerWorker(LocalWorker(i, 'wrk%i' % i))

scheduler.start()

queue = RenderQueue()

class TestListener(RenderListener):
  def workBeginEvent (self, job, wu, worker):
    print 'workBeginEvent'
    return
  def workEndEvent (self, job, wr, cancelled):
    print 'workEndEvent'
    return
  def workCanceledEvent (self, job, offset, size):
    print 'workCanceledEvent'
    return
  def refreshEvent (self, job):
    print 'refreshEvent'
    return
  def finishJobEvent (self, job, cancelled):
    print 'finishJobEvent ' + 'cancelled : ' + str(cancelled)
    return

testListener = TestListener()
queue.registerListener( testListener )

pmgr = PluginManager.getInstance()

# Create a simple scene containing a sphere
sphere = pmgr.createObject(Properties("sphere"))
sphere.configure()
scene = Scene()
scene.addChild(sphere)
scene.configure()
scene.setDestinationFile('test_file')

# Create a render job and insert it into the queue
job = RenderJob('myRenderJob', scene, queue)
job.start()
# Wait for the job to finish
queue.waitLeft(0)

# workBeginEvent
# workBeginEvent
# workBeginEvent
# workEndEvent
# workBeginEvent
# ...
# workEndEvent
# workBeginEvent
# workEndEvent
# workEndEvent
# workEndEvent

# Rendering: [++++++++++++++++++++++++++++++++++++++++++++] (0.5s, ETA: -0.0s)
# workEndEvent
# INFO  myRenderJob[RenderJob] Render time: 0.5410s
# INFO  myRenderJob[HDRFilm] Writing image to "test_file.exr" ..
# finishJobEvent cancelled : False

 たぶんRenderJobがエラーでとまった時は、finishJobEventcancelledTrueになるんだと思います(未確認)