[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [22865] branches/blender2.5/blender/ release/io: First commit draft for network rendering.

Martin Poirier theeth at yahoo.com
Sat Aug 29 19:25:22 CEST 2009


Revision: 22865
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=22865
Author:   theeth
Date:     2009-08-29 19:25:22 +0200 (Sat, 29 Aug 2009)

Log Message:
-----------
First commit draft for network rendering.

Docs are here: http://wiki.blender.org/index.php/User:Theeth/netrender

Should be easy to test if people want too, just follow the instructions on wiki

Code is still very much in flux, so I'd like if people would refrain from making changes (send patches directly to me if you must).

The UI side is very crap, it's basically there just to get things testable. See wiki for known bugs.

Added Paths:
-----------
    branches/blender2.5/blender/release/io/netrender/
    branches/blender2.5/blender/release/io/netrender/__init__.py
    branches/blender2.5/blender/release/io/netrender/client.py
    branches/blender2.5/blender/release/io/netrender/master.py
    branches/blender2.5/blender/release/io/netrender/model.py
    branches/blender2.5/blender/release/io/netrender/operators.py
    branches/blender2.5/blender/release/io/netrender/slave.py
    branches/blender2.5/blender/release/io/netrender/ui.py
    branches/blender2.5/blender/release/io/netrender/utils.py

Added: branches/blender2.5/blender/release/io/netrender/__init__.py
===================================================================
--- branches/blender2.5/blender/release/io/netrender/__init__.py	                        (rev 0)
+++ branches/blender2.5/blender/release/io/netrender/__init__.py	2009-08-29 17:25:22 UTC (rev 22865)
@@ -0,0 +1,9 @@
+# This directory is a Python package.
+
+import model
+import operators
+import client
+import slave
+import master
+import utils
+import ui

Added: branches/blender2.5/blender/release/io/netrender/client.py
===================================================================
--- branches/blender2.5/blender/release/io/netrender/client.py	                        (rev 0)
+++ branches/blender2.5/blender/release/io/netrender/client.py	2009-08-29 17:25:22 UTC (rev 22865)
@@ -0,0 +1,87 @@
+import bpy
+import sys, os
+import http, http.client, http.server, urllib
+import subprocess, shutil, time, hashlib
+
+import netrender.slave as slave
+import netrender.master as master
+from netrender.utils import *
+
+class NetworkRenderEngine(bpy.types.RenderEngine):
+	__idname__ = 'NET_RENDER'
+	__label__ = "Network Render"
+	def render(self, scene):
+		if scene.network_render.mode == "RENDER_CLIENT":
+			self.render_client(scene)
+		elif scene.network_render.mode == "RENDER_SLAVE":
+			self.render_slave(scene)
+		elif scene.network_render.mode == "RENDER_MASTER":
+			self.render_master(scene)
+		else:
+			print("UNKNOWN OPERATION MODE")
+	
+	def render_master(self, scene):
+		server_address = (scene.network_render.server_address, scene.network_render.server_port)
+		httpd = master.RenderMasterServer(server_address, master.RenderHandler)
+		httpd.timeout = 1
+		httpd.stats = self.update_stats
+		while not self.test_break():
+			httpd.handle_request()
+
+	def render_slave(self, scene):
+		slave.render_slave(self, scene)
+	
+	def render_client(self, scene):
+		self.update_stats("", "Network render client initiation")
+		
+		conn = clientConnection(scene)
+		
+		if conn:
+			# Sending file
+			
+			self.update_stats("", "Network render exporting")
+			
+			job_id = scene.network_render.job_id
+			
+			# reading back result
+			
+			self.update_stats("", "Network render waiting for results")
+			
+			clientRequestResult(conn, scene, job_id)
+			response = conn.getresponse()
+			
+			if response.status == http.client.NO_CONTENT:
+				scene.network_render.job_id = clientSendJob(conn, scene)
+				clientRequestResult(conn, scene, job_id)
+			
+			while response.status == http.client.PROCESSING and not self.test_break():
+				print("waiting")
+				time.sleep(1)
+				clientRequestResult(conn, scene, job_id)
+				response = conn.getresponse()
+	
+			if response.status != http.client.OK:
+				conn.close()
+				return
+			
+			r = scene.render_data
+			x= int(r.resolution_x*r.resolution_percentage*0.01)
+			y= int(r.resolution_y*r.resolution_percentage*0.01)
+			
+			f = open(PATH_PREFIX + "output.exr", "wb")
+			buf = response.read(1024)
+			
+			while buf:
+				f.write(buf)
+				buf = response.read(1024)
+			
+			f.close()
+			
+			result = self.begin_result(0, 0, x, y)
+			result.load_from_file(PATH_PREFIX + "output.exr", 0, 0)
+			self.end_result(result)
+			
+			conn.close()
+
+bpy.types.register(NetworkRenderEngine)
+

Added: branches/blender2.5/blender/release/io/netrender/master.py
===================================================================
--- branches/blender2.5/blender/release/io/netrender/master.py	                        (rev 0)
+++ branches/blender2.5/blender/release/io/netrender/master.py	2009-08-29 17:25:22 UTC (rev 22865)
@@ -0,0 +1,546 @@
+import sys, os
+import http, http.client, http.server, urllib
+import subprocess, shutil, time, hashlib
+
+from netrender.utils import *
+import netrender.model
+
+		
+class MRenderSlave(netrender.model.RenderSlave):
+	def __init__(self, name, adress, stats):
+		super().__init__()
+		self.id = hashlib.md5(bytes(repr(name) + repr(adress), encoding='utf8')).hexdigest()
+		self.name = name
+		self.adress = adress
+		self.stats = stats
+		self.last_seen = time.time()
+		
+		self.job = None
+		self.frame = None
+		
+		netrender.model.RenderSlave._slave_map[self.id] = self
+
+	def seen(self):
+		self.last_seen = time.time()
+
+# sorting key for jobs
+def groupKey(job):
+	return (job.framesLeft() > 0, job.priority, job.credits)
+
+class MRenderJob(netrender.model.RenderJob):
+	def __init__(self, job_id, name, path, chunks = 1, priority = 1, credits = 100.0, blacklist = []):
+		super().__init__()
+		self.id = job_id
+		self.name = name
+		self.path = path
+		self.frames = []
+		self.chunks = chunks
+		self.priority = priority
+		self.credits = credits
+		self.blacklist = blacklist
+		self.last_dispatched = time.time()
+	
+	def update(self):
+		self.credits -= 5 # cost of one frame
+		self.credits += (time.time() - self.last_dispatched) / 60
+		self.last_dispatched = time.time()
+	
+	def addFrame(self, frame_number):
+		frame = MRenderFrame(frame_number)
+		self.frames.append(frame)
+		return frame
+	
+	def framesLeft(self):
+		total = 0
+		for j in self.frames:
+			if j.status == QUEUED:
+				total += 1
+		
+		return total
+		
+	def reset(self, all):
+		for f in self.frames:
+			f.reset(all)
+	
+	def getFrames(self):
+		frames = []
+		for f in self.frames:
+			if f.status == QUEUED:
+				self.update()
+				frames.append(f)
+				if len(frames) == self.chunks:
+					break
+		
+		return frames
+
+class MRenderFrame(netrender.model.RenderFrame):
+	def __init__(self, frame):
+		super().__init__()
+		self.number = frame
+		self.slave = None
+		self.time = 0
+		self.status = QUEUED
+		
+	def reset(self, all):
+		if all or self.status == ERROR:
+			self.slave = None
+			self.time = 0
+			self.status = QUEUED
+
+
+# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+class RenderHandler(http.server.BaseHTTPRequestHandler):
+	def send_head(self, code = http.client.OK, headers = {}):
+		self.send_response(code)
+		self.send_header("Content-type", "application/octet-stream")
+		
+		for key, value in headers.items():
+			self.send_header(key, value)
+		
+		self.end_headers()
+
+	def do_HEAD(self):
+		print(self.path)
+	
+		if self.path == "status":
+			job_id = self.headers.get('job-id', "")
+			job_frame = int(self.headers.get('job-frame', -1))
+			
+			if job_id:
+				print("status:", job_id, "\n")
+				
+				job = self.server.getJobByID(job_id)
+				if job:
+					if job_frame != -1:
+						frame = job[frame]
+						
+						if not frame:
+							# no such frame
+							self.send_heat(http.client.NOT_FOUND)
+							return
+				else:
+					# no such job id
+					self.send_head(http.client.NOT_FOUND)
+					return
+			
+			self.send_head()
+	
+	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+	# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+	# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+	
+	def do_GET(self):
+		print(self.path)
+		
+		if self.path == "version":
+			self.send_head()
+			self.server.stats("", "New client connection")
+			self.wfile.write(VERSION)
+		# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+		elif self.path == "render":
+			job_id = self.headers['job-id']
+			job_frame = int(self.headers['job-frame'])
+			print("render:", job_id, job_frame)
+			
+			job = self.server.getJobByID(job_id)
+			
+			if job:
+				frame = job[job_frame]
+				
+				if frame:
+					if frame.status in (QUEUED, DISPATCHED):
+						self.send_head(http.client.PROCESSING)
+					elif frame.status == DONE:
+						self.server.stats("", "Sending result back to client")
+						f = open(PATH_PREFIX + job_id + "%04d" % job_frame + ".exr", 'rb')
+						
+						self.send_head()
+						
+						shutil.copyfileobj(f, self.wfile)
+						
+						f.close()
+					elif frame.status == ERROR:
+						self.send_head(http.client.NO_CONTENT)
+				else:
+					# no such frame
+					self.send_head(http.client.NOT_FOUND)
+			else:
+				# no such job id
+				self.send_head(http.client.NOT_FOUND)
+		# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+		elif self.path == "log":
+			job_id = self.headers['job-id']
+			job_frame = int(self.headers['job-frame'])
+			print("log:", job_id, job_frame)
+			
+			job = self.server.getJobByID(job_id)
+			
+			if job:
+				frame = job[job_frame]
+				
+				if frame:
+					if frame.status in (QUEUED, DISPATCHED):
+						self.send_head(http.client.PROCESSING)
+					elif frame.status == DONE:
+						self.server.stats("", "Sending log back to client")
+						f = open(PATH_PREFIX + job_id + "%04d" % job_frame + ".log", 'rb')
+						
+						self.send_head()
+						
+						shutil.copyfileobj(f, self.wfile)
+						
+						f.close()
+					elif frame.status == ERROR:
+						self.send_head(http.client.NO_CONTENT)
+				else:
+					# no such frame
+					self.send_head(http.client.NOT_FOUND)
+			else:
+				# no such job id
+				self.send_head(http.client.NOT_FOUND)
+		# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+		elif self.path == "status":
+			job_id = self.headers.get('job-id', "")
+			job_frame = int(self.headers.get('job-frame', -1))
+			
+			if job_id:
+				print("status:", job_id, "\n")
+				
+				job = self.server.getJobByID(job_id)
+				if job:
+					if job_frame != -1:
+						frame = job[frame]
+						
+						if frame:
+							message = frame.serialize()
+						else:
+							# no such frame
+							self.send_heat(http.client.NOT_FOUND)
+							return
+					else:
+						message = job.serialize()
+				else:
+					# no such job id
+					self.send_head(http.client.NOT_FOUND)
+					return
+			else: # status of all jobs
+				message = []
+				
+				for job in self.server:
+					results = job.status()

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list