blender/release/io/netrender/client.py
Martin Poirier 22274d3807 More automatic stuff.
Server can now be set to broadcast on local network (every 10s, approximately 20 bytes of data) where client and slave can pick up its existence. This is on by default.

Default ip address is now "[default]", which means for the master that it will listen to all interface and for the clients and slave that they will automatically work out the master's address from its broadcast.
2009-09-15 19:53:18 +00:00

205 lines
5.6 KiB
Python

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 *
def clientSendJob(conn, scene, anim = False, chunks = 5):
netsettings = scene.network_render
job = netrender.model.RenderJob()
if anim:
for f in range(scene.start_frame, scene.end_frame + 1):
job.addFrame(f)
else:
job.addFrame(scene.current_frame)
filename = bpy.data.filename
job.addFile(filename)
job_name = netsettings.job_name
path, name = os.path.split(filename)
if job_name == "[default]":
job_name = name
###########################
# LIBRARIES
###########################
for lib in bpy.data.libraries:
lib_path = lib.filename
if lib_path.startswith("//"):
lib_path = path + os.sep + lib_path[2:]
job.addFile(lib_path)
###########################
# POINT CACHES
###########################
root, ext = os.path.splitext(name)
cache_path = path + os.sep + "blendcache_" + root + os.sep # need an API call for that
if os.path.exists(cache_path):
caches = {}
pattern = re.compile("([a-zA-Z0-9]+)_([0-9]+)_[0-9]+\.bphys")
for cache_file in sorted(os.listdir(cache_path)):
match = pattern.match(cache_file)
if match:
cache_id = match.groups()[0]
cache_frame = int(match.groups()[1])
cache_files = caches.get(cache_id, [])
cache_files.append((cache_frame, cache_file))
caches[cache_id] = cache_files
for cache in caches.values():
cache.sort()
if len(cache) == 1:
cache_frame, cache_file = cache[0]
job.addFile(cache_path + cache_file, cache_frame, cache_frame)
else:
for i in range(len(cache)):
current_item = cache[i]
next_item = cache[i+1] if i + 1 < len(cache) else None
previous_item = cache[i - 1] if i > 0 else None
current_frame, current_file = current_item
if not next_item and not previous_item:
job.addFile(cache_path + current_file, current_frame, current_frame)
elif next_item and not previous_item:
next_frame = next_item[0]
job.addFile(cache_path + current_file, current_frame, next_frame - 1)
elif not next_item and previous_item:
previous_frame = previous_item[0]
job.addFile(cache_path + current_file, previous_frame + 1, current_frame)
else:
next_frame = next_item[0]
previous_frame = previous_item[0]
job.addFile(cache_path + current_file, previous_frame + 1, next_frame - 1)
###########################
# IMAGES
###########################
for image in bpy.data.images:
if image.source == "FILE" and not image.packed_file:
job.addFile(image.filename)
# print(job.files)
job.name = job_name
for slave in scene.network_render.slaves_blacklist:
job.blacklist.append(slave.id)
job.chunks = netsettings.chunks
job.priority = netsettings.priority
# try to send path first
conn.request("POST", "job", repr(job.serialize()))
response = conn.getresponse()
job_id = response.getheader("job-id")
# if not ACCEPTED (but not processed), send files
if response.status == http.client.ACCEPTED:
for filepath, start, end in job.files:
f = open(filepath, "rb")
conn.request("PUT", "file", f, headers={"job-id": job_id, "job-file": filepath})
f.close()
response = conn.getresponse()
# server will reply with NOT_FOUD until all files are found
return job_id
def requestResult(conn, job_id, frame):
conn.request("GET", "render", headers={"job-id": job_id, "job-frame":str(frame)})
@rnaType
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):
netsettings = scene.network_render
address = "" if netsettings.server_address == "[default]" else netsettings.server_address
master.runMaster((address, netsettings.server_port), netsettings.server_broadcast, netsettings.path, self.update_stats, self.test_break)
def render_slave(self, scene):
slave.render_slave(self, scene)
def render_client(self, scene):
netsettings = scene.network_render
self.update_stats("", "Network render client initiation")
conn = clientConnection(scene)
if conn:
# Sending file
self.update_stats("", "Network render exporting")
job_id = netsettings.job_id
# reading back result
self.update_stats("", "Network render waiting for results")
requestResult(conn, job_id, scene.current_frame)
response = conn.getresponse()
if response.status == http.client.NO_CONTENT:
netsettings.job_id = clientSendJob(conn, scene)
requestResult(conn, job_id, scene.current_frame)
while response.status == http.client.ACCEPTED and not self.test_break():
print("waiting")
time.sleep(1)
requestResult(conn, job_id, scene.current_frame)
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(netsettings.path + "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(netsettings.path + "output.exr", 0, 0)
self.end_result(result)
conn.close()