def _setParam(self,scn,context,partindex,nparts): """ Initializes scene and context for rendering a given part. Also saves the altered parameters of scene and context for later restoration by L{_resetParam()}. See L{NetworkRender.StillRenderThread} for typical use. @param scn: the current scene @type scn : Blender.Scene @param context: the current render context @type context: Scene.Renderdata @param partindex: the number of parts in either direction @type partindex: int @param nparts: the number of parts in either direction @type nparts: int. @requires: nparts = 2,3,4,5, .... @requires: partindex = [0, nparts^2 > """ self.nparts = nparts self.cam = Camera.Get(scn.getCurrentCamera().getName()) self.scn = scn self.depth = context.imagePlanes self.aspx = float(context.sizeX) / float(context.sizeY) self.aspy = float(context.sizeY) / float(context.sizeX) self.camipo = None if self.cam.ipo: self.camipo = self.cam.ipo self.cam.ipo = None self.lens = self.cam.lens self.scale = self.cam.scale self.shiftX = self.cam.shiftX self.shiftY = self.cam.shiftY self.path = context.renderPath self.cam.scale = (self.scale / (nparts + 1)) self.cam.lens = (self.lens * (nparts)) tileStart = [ i * -0.5 for i in range(nparts + 1)] print partindex, nparts, tileStart y = int(partindex/nparts) x = partindex%nparts shiftY = (-tileStart[nparts - 1] - float(y)) shiftX = (float(x) + tileStart[nparts - 1]) if self.aspx > 1.0 : shiftY /= self.aspx else : shiftX /= self.aspy self.cam.shiftX = shiftX self.cam.shiftY = shiftY debug('x=%d y=%d shiftX=%4.2f shiftY=%4.2f index=%d nparts%d'%(x, y, shiftX, shiftY, partindex, nparts))
def renderPart(self, scenename, partindex, nparts, imageType): """ Render a single part of a multipart still. @scenename: the name of the scene to render @partindex: the partnumber to render ( 0 <= partindex < nparts^2 ) @nparts : the number of parts a still is divided in both directions @imageType: type of output image Based on Macouno's Really Big Render ( http://www.alienhelpdesk.com/python_scripts/really_big_render ) Kudos to him, implementation errors are entirely mine. See NetworkRender.PartRenderer for additional info. """ import bpy lib = bpy.libraries.load(self.name) print self.name,' loaded' scn = lib.scenes.link(scenename) context = scn.getRenderingContext() print 'remote render start part',partindex context.displayMode = 0 # to prevent an additional render window popping up self._PartName(partindex, nparts) # change camera related stuff self._setParam(scn, context, partindex, nparts) scn.update() context.renderPath = self.result f = context.currentFrame() # remember to restore later! s,context.sFrame = context.sFrame,f e,context.eFrame = context.eFrame,f oldImagetype = context.imageType oldWidth, oldHeight = context.sizeX, context.sizeY oldRenderPath = context.getRenderPath() context.imageType = imageType context.sizeX /= self.nparts context.sizeY /= self.nparts context.setRenderPath(configurer.get('ServerRenderPath')) debug('current=%d start=%d end=%d' % (f, context.sFrame, context.eFrame)) debug('start render') context.renderPath = self.result context.renderAnim() # because .render doesn't work in the background self.result = context.getFrameFilename() # Restore changed settings context.sFrame,context.eFrame = s,e context.imageType = oldImagetype context.setRenderPath(oldRenderPath) context.sizeX, context.sizeY = oldWidth, oldHeight self._resetParam(scn,context) print 'remote render end part',partindex return 'render finished'
def _sendBlendFile(self): """ Sent saved .blendfile if needed. Uploads saved .blend file to remote host if not already done so. """ if self.isLocal() or self.blendSent: pass else: self.rpcserver.newfile() fd = open(self.name, 'rb', self.configurer.get('ServerBufferSize')) debug('%s sending .blend: %s'%(self.uri, fd)) n = 0 buffer = True while buffer : buffer = fd.read(self.configurer.get('ServerBufferSize')) if buffer: r = self.rpcserver.put(Binary(buffer)) debug('%s put response %s' % (self.uri, r)) n = n + 1 debug('%s %d blocks put'%(self.uri, n)) fd.close() r = self.rpcserver.endfile() debug('%s endfile called, response %s'%(self.uri, r)) self.blendSent = True
def registerNode(self, uri): """ Register rendering node """ nodeData = self.uriMap.get(uri) if (nodeData == None or self.reusable(uri)): debug('spawning new thread for %s' % uri) rt = self.factory(uri, self.scenename, self.context, self.name, self.fqueue, self.squeue,*self.args) self.r.append(rt) self.uriMap[uri] = {'renderThread': rt} rt.start()
def _renderFrame(self): debug('%s render started for frame %d'%(self.uri,self.frame)) if self.isLocal(): self.context.currentFrame(self.frame) # remember to restore later! s,self.context.sFrame = self.context.sFrame,self.frame e,self.context.eFrame = self.context.eFrame,self.frame oldImagetype = self.context.imageType self.context.imageType = self.imageType self.context.renderAnim() self.result = self.context.getFrameFilename() # Restore changed settings self.context.sFrame,self.context.eFrame = s,e self.context.imageType = oldImagetype else: self.rpcserver.renderFrame(self.scenename, self.frame, self.imageType) debug('%s render completed for frame %d'%(self.uri, self.frame))
def reusable(self, uri): nodeData = self.uriMap.get(uri) rt = nodeData['renderThread'] # Check is thread still alive and not failed if (not rt.serviceAlive()): # Thread is not alive because of remote server caught an # unhandled exception. But is this case remote server freezes. # So, a ping pocket from this server means this server is restarted # and we could reuse this thread # It would be safer to manually request stopping of thread rt.requestStop() debug('node uri %s caught a failure, but send a ping pocket again.') return True return False
def broadcast_uri(self): from time import sleep global running, configurer bcast = configurer.get('ServerBCast') port = configurer.get('ClientPort') delay = configurer.get('ServerBCastInterval') while running: # Broadcast server's address if (bcast != ''): debug('Broadcast server URI %s to all clients on %s:%d' % (self.uri, bcast, port)) self.broadcast.sendto(self.uri, (bcast, port)) # Try to send server's data to clients from static map for staticClient in self.staticMap: map = self.staticMap[staticClient] debug('Send server URI %s to client %s:%d' % (map['serverURI'], staticClient, port)) self.broadcast.sendto(map['serverURI'], (staticClient, port)) sleep(delay)
def run(self): """ Run as long as there is work available in the workqueue. If an exception is caught while rendering, the workitem is put back on the worklist and a statistic is recorded as a failed render. @warning: Note that this might end up as an endless loop if a remote server continues to fail and the localhost thread is never fast enough to snatch the workitem from the queue. This needs some work! """ # this might not be correct, get(nonblocking) better? while not self.stop and not self.frames.empty(): debug('%s retrieving frame from queue' % self.uri) frame = self.frames.get() ts = time.time() debug('%s got frame %d' % (self.uri,frame)) try: self.render(frame) # provided by mixin/subclass except (xmlrpclib.Error, socket.error),e: print 'remote exception caught',e print 'requeueing frame',frame # Need this for correct joining to the frame queue # (formally task is done and queue after requeueing of buggy # frame it will be another task) self.frames.task_done() self.frames.put(frame) te = time.time() self.stats.put((self.uri, frame, te - ts, 1, 'none')) self.failure = True break te = time.time() self.frames.task_done() self.stats.put((self.uri, frame, te - ts, 0, self.result))
def __init__(self, address, handler): global configurer SimpleXMLRPCServer.__init__(self, address, handler) self.broadcast = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Allow the socket to broadcast, set e socket options. self.broadcast.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) self.ip = configurer.get('ServerAddr'); if (self.ip == '0.0.0.0'): self.ip = socket.gethostbyname(socket.gethostname()) self.uri = 'http://' + self.ip + ':' + str(configurer.get('ServerPort')) self.staticMap = {} sMap = configurer.get('ServerStaticMap') debug('Parsing static server string specification "%s"...' % (sMap)) dummyMap = sMap.split(',') for x in dummyMap: if (x.strip() == ''): continue debug('Parsing static server data "%s"...' % (x)) dummy = x.split(':') if (len(dummy) == 1): dummy.append(self.ip) if (len(dummy) != 2): continue self.staticMap[dummy[0]] = {'serverIP': dummy[1].strip(), 'serverURI': 'http://' + dummy[1].strip() + ':' + str(configurer.get('ServerPort'))}
def run(self): """ Listen and spawn new worker threads if appropriate. Overridden from Thread. Implements a stoppable loop (stops when requestStop is called on this thread) and records with workerthreads were spawned. """ import time self.stop = False self.r = [] self.uriMap = {} # Register static list of server servers = self.configurer.get('ClientServerList').split(',') for server in servers: if (server == ''): continue serverData = server.split(':') if (len (serverData) == 1): serverData.append(str(self.configurer.get('ServerPort'))) self.registerNode('http://' + serverData[0].strip() + ':' + serverData[1].strip()) while not self.stop: debug('UDPlistener ready for request on %s,%s' % self.socket.getsockname()) try: self.socket.settimeout(5) data, addr = self.socket.recvfrom(512) debug('UDPlistener received request: %s from %s' % (data, addr)) self.registerNode(data) except (socket.timeout) : debug('UDPlistener received nothing, will try again') finally: time.sleep(self.configurer.get('ServerBCastInterval')) debug('UDPlistener stopped')
def __init__(self, scenename, context, name, fqueue, squeue, threadfactory, *args): """ Initialize a Listener instance. @param scenename: name of current scene @type scenename: string @param context: rendercontext of current scene @type context: Blender.Renderdata @param name: filename of saved .blend file @type name: string @param fqueue: worklist queue @type fqueue: Queue.queue @param squeue: statistics queue @type squeue: Queue.queue @param threadfactory: a classfactory to spawn worker threads @type threadfactory: NetworkRender.Renderthread @param args: additional arguments to be given to the threadfactory """ Thread.__init__(self) self.scenename = scenename self.context = context self.name = name self.factory = threadfactory self.fqueue = fqueue self.squeue = squeue self.args = args self.configurer = Configurer() debug('UDPlistener starting') self.socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) debug('UDPlistener socket created %s' % self.socket) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) debug('UDPlistener options set') #self.ip = socket.gethostbyname(socket.gethostname()) self.ip = '0.0.0.0' self.socket.bind((self.ip, self.configurer.get('ClientPort'))) debug('UDPlistener listening on %s'%self.socket)
# the worklist (either part- or framenumbers) frames = Queue() # statistics are communicated by the renderthreads via this queue stats = Queue() # lets start! starttime = time.time() # save the current .blend (scn, context, scenename, name) = NetworkRender.saveblend() # initialize the worklist for frame in range(parts * parts): debug('queueing frame %d' %frame) frames.put(frame) # start listening for remote servers from NetworkRender.Listener import Listener listener = Listener(scenename, context, name, frames, \ stats, StillRenderThread, parts, imageType) listener.start() # create a local renderer (we wont let others do all the dirty work :-) if localRendering: localrenderer = StillRenderThread('localhost', scenename, context, \ name, frames, stats, parts, imageType) # start the local renderer if localRendering:
def _renderStill(self): debug('%s render started for frame %d'%(self.uri,self.frame)) if self.isLocal(): self._PartName(self.frame, self.nparts) debug('partname set') self._setParam(self.scn, self.context, self.frame, self.nparts) debug('setparam done') self.scn.update() self.context.renderPath = self.result debug('renderpath before %s'%self.context.renderPath) f = self.context.currentFrame() # remember to restore later! s,self.context.sFrame = self.context.sFrame,f e,self.context.eFrame = self.context.eFrame,f oldImagetype = self.context.imageType oldWidth, oldHeight = self.context.sizeX, self.context.sizeY debug('current=%d start=%d end=%d' % (f, self.context.sFrame, self.context.eFrame)) debug('start render') self.context.imageType = self.imageType self.context.sizeX /= self.nparts self.context.sizeY /= self.nparts self.context.renderAnim() # because .render doesn't work in the background self.result = self.context.getFrameFilename() # Restore changed settings self.context.sFrame,self.context.eFrame = s,e self.context.imageType = oldImagetype self.context.sizeX,self.context.sizeY = oldWidth, oldHeight #self.context.saveRenderedImage(self.result, 0) debug('resetparam') self._resetParam(self.scn, self.context) debug('renderpath after %s'%self.context.renderPath) else: self.rpcserver.renderPart(self.scenename, self.frame, \ self.nparts, self.imageType) debug('%s render completed for frame %d'%(self.uri, self.frame))
def _getResult(self): """ Download rendered frame or part from remote server. """ debug('%s saving frame %d'%(self.uri, self.frame)) if not self.isLocal(): debug('%s getting remote frame %d'%(self.uri, self.frame)) rname = self.rpcserver.getResult() from tempfile import mkstemp import os from os import path remotedir,remotefile = path.split(rname) if self.animation == True : localdir,localfile = path.split(self.context.getFrameFilename()) name = path.join(localdir,remotefile) fd,tname = mkstemp(suffix = remotefile) os.close(fd) debug('%s saving remote frame %d as tempfile %s' % (self.uri, self.frame, tname)) fd = open(tname,'wb',self.configurer.get('ServerBufferSize')) while True: data = str(self.rpcserver.get()) if len(data) <= 0: fd.close() break else: fd.write(str(data)) fd.close() if self.animation == True : debug('%s frame %d renaming %s to %s'%(self.uri,self.frame, tname, name)) if os.access(name, os.F_OK): os.unlink(name) # windows won't let us rename to an existing file debug("%s %s exists? %s" % (self.uri,tname, os.access(tname, os.F_OK))) os.rename(tname,name) # why all this trouble? the source file on the serverside might # be on the exact same location as the file we try to write # on the clientside if we run the server and the on the same # machine (as we might do for testing) self.result = name else : self.result = tname print 'Saved: %s (remotely rendered)'% self.result else: print 'Saved: %s (locally rendered)'% self.result pass debug('%s saving frame %d done'%(self.uri,self.frame))
# the worklist (either part- or framenumbers) frames = Queue() # statistics are communicated by the renderthreads via this queue stats = Queue() # lets start! starttime = time.time() # save the current .blend (scn, context, scenename, name) = NetworkRender.saveblend() # initialize the worklist for frame in range(context.sFrame, context.eFrame + 1): debug("queueing frame %d" % frame) frames.put(frame) # start listening for remote servers from NetworkRender.Listener import Listener listener = Listener(scenename, context, name, frames, stats, AnimRenderThread, imageType) listener.start() # create a local renderer (we wont let others do all the dirty work :-) if localRendering: localrenderer = AnimRenderThread("localhost", scenename, context, name, frames, stats, imageType) # start the local renderer if localRendering: localrenderer.start()