forked from bartvdbraak/blender
22274d3807
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.
205 lines
5.6 KiB
Python
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()
|
|
|