diff --git a/release/io/netrender/client.py b/release/io/netrender/client.py index d059387cfcf..a6cfb4e020d 100644 --- a/release/io/netrender/client.py +++ b/release/io/netrender/client.py @@ -138,12 +138,12 @@ class NetworkRenderEngine(bpy.types.RenderEngine): 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, scene.network_render.path) - httpd.timeout = 1 - httpd.stats = self.update_stats - while not self.test_break(): - httpd.handle_request() + 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) @@ -152,6 +152,7 @@ class NetworkRenderEngine(bpy.types.RenderEngine): netsettings = scene.network_render self.update_stats("", "Network render client initiation") + conn = clientConnection(scene) if conn: diff --git a/release/io/netrender/master.py b/release/io/netrender/master.py index 78e9243bc9d..13e8b399d6c 100644 --- a/release/io/netrender/master.py +++ b/release/io/netrender/master.py @@ -1,5 +1,5 @@ import sys, os -import http, http.client, http.server, urllib +import http, http.client, http.server, urllib, socket import subprocess, shutil, time, hashlib from netrender.utils import * @@ -529,7 +529,7 @@ class RenderHandler(http.server.BaseHTTPRequestHandler): job_frame = int(self.headers['job-frame']) buf = self.rfile.read(length) - f = open(job.save_path + "%04d" % job_frame + ".log", 'wb') + f = open(job.save_path + "%04d" % job_frame + ".log", 'ab') f.write(buf) f.close() @@ -613,3 +613,23 @@ class RenderMasterServer(http.server.HTTPServer): return job, job.getFrames() return None, None + +def runMaster(address, broadcast, path, update_stats, test_break): + httpd = RenderMasterServer(address, RenderHandler, path) + httpd.timeout = 1 + httpd.stats = update_stats + + if broadcast: + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + + start_time = time.time() + + while not test_break(): + httpd.handle_request() + + if broadcast: + if time.time() - start_time >= 10: # need constant here + print("broadcasting address") + s.sendto(bytes("%s:%i" % address, encoding='utf8'), 0, ('',address[1])) + start_time = time.time() diff --git a/release/io/netrender/operators.py b/release/io/netrender/operators.py index 655afa6631f..928c2b9efaf 100644 --- a/release/io/netrender/operators.py +++ b/release/io/netrender/operators.py @@ -1,6 +1,6 @@ import bpy import sys, os -import http, http.client, http.server, urllib +import http, http.client, http.server, urllib, socket from netrender.utils import * import netrender.client as client @@ -316,4 +316,41 @@ class netclientdownload(bpy.types.Operator): return ('FINISHED',) def invoke(self, context, event): - return self.execute(context) \ No newline at end of file + return self.execute(context) + +@rnaOperator +class netclientscan(bpy.types.Operator): + '''Operator documentation text, will be used for the operator tooltip and python docs.''' + __idname__ = "render.netclientscan" + __label__ = "Net Render Client Scan" + + # List of operator properties, the attributes will be assigned + # to the class instance from the operator settings before calling. + + __props__ = [] + + def poll(self, context): + return True + + def execute(self, context): + netsettings = context.scene.network_render + + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + s.settimeout(30) + + s.bind(('', netsettings.server_port)) + + try: + buf, address = s.recvfrom(128) + + print("received:", buf) + + netsettings.server_address = address[0] + except socket.timeout: + print("no server info") + + return ('FINISHED',) + + def invoke(self, context, event): + return self.execute(context) diff --git a/release/io/netrender/slave.py b/release/io/netrender/slave.py index c12c846231d..1dcb608931e 100644 --- a/release/io/netrender/slave.py +++ b/release/io/netrender/slave.py @@ -105,6 +105,8 @@ def render_slave(engine, scene): process = subprocess.Popen([sys.argv[0], "-b", job_full_path, "-o", JOB_PREFIX + "######", "-E", "BLENDER_RENDER", "-F", "MULTILAYER"] + frame_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + headers = {"job-id":job.id, "slave-id":slave_id} + cancelled = False stdout = bytes() run_t = time.time() @@ -113,10 +115,18 @@ def render_slave(engine, scene): current_t = time.time() cancelled = engine.test_break() if current_t - run_t > CANCEL_POLL_SPEED: + + # update logs. Eventually, it should support one log file for many frames + for frame in job.frames: + headers["job-frame"] = str(frame.number) + conn.request("PUT", "log", stdout, headers=headers) + response = conn.getresponse() + + stdout = bytes() + + run_t = current_t if testCancel(conn, job.id): cancelled = True - else: - run_t = current_t if cancelled: # kill process if needed @@ -132,6 +142,13 @@ def render_slave(engine, scene): print("status", status) + # flush the rest of the logs + if stdout: + for frame in job.frames: + headers["job-frame"] = str(frame.number) + conn.request("PUT", "log", stdout, headers=headers) + response = conn.getresponse() + headers = {"job-id":job.id, "slave-id":slave_id, "job-time":str(avg_t)} if status == 0: # non zero status is error @@ -150,12 +167,6 @@ def render_slave(engine, scene): # send error result back to server conn.request("PUT", "render", headers=headers) response = conn.getresponse() - - for frame in job.frames: - headers["job-frame"] = str(frame.number) - # send log in any case - conn.request("PUT", "log", stdout, headers=headers) - response = conn.getresponse() else: if timeout < MAX_TIMEOUT: timeout += INCREMENT_TIMEOUT diff --git a/release/io/netrender/ui.py b/release/io/netrender/ui.py index eee95bdac19..df2b6288fb0 100644 --- a/release/io/netrender/ui.py +++ b/release/io/netrender/ui.py @@ -48,17 +48,24 @@ class SCENE_PT_network_settings(RenderButtonsPanel): col = split.column() - col.itemO("render.netclientanim", icon='ICON_RENDER_ANIMATION', text="Animaton on network") + if scene.network_render.mode == "RENDER_CLIENT": + col.itemO("render.netclientanim", icon='ICON_RENDER_ANIMATION', text="Animaton on network") + col.itemR(scene.network_render, "mode") + col.itemR(scene.network_render, "path") col.itemR(scene.network_render, "server_address") col.itemR(scene.network_render, "server_port") - col.itemR(scene.network_render, "path") + + if scene.network_render.mode == "RENDER_MASTER": + col.itemR(scene.network_render, "server_broadcast") + else: + col.itemO("render.netclientscan", icon="ICON_FILE_REFRESH", text="") if scene.network_render.mode == "RENDER_CLIENT": - col.itemR(scene.network_render, "chunks") - col.itemR(scene.network_render, "priority") - col.itemR(scene.network_render, "job_name") col.itemO("render.netclientsend", text="send job to server") + col.itemR(scene.network_render, "job_name") + col.itemR(scene.network_render, "priority") + col.itemR(scene.network_render, "chunks") @rnaType class SCENE_PT_network_slaves(RenderButtonsPanel): @@ -192,7 +199,7 @@ NetRenderSettings.StringProperty( attr="server_address", name="Server address", description="IP or name of the master render server", maxlen = 128, - default = "127.0.0.1") + default = "[default]") NetRenderSettings.IntProperty( attr="server_port", name="Server port", @@ -201,6 +208,11 @@ NetRenderSettings.IntProperty( attr="server_port", min=1, max=65535) +NetRenderSettings.BoolProperty( attr="server_broadcast", + name="Broadcast server address", + description="broadcast server address on local network", + default = True) + NetRenderSettings.StringProperty( attr="path", name="Path", description="Path for temporary files", diff --git a/release/io/netrender/utils.py b/release/io/netrender/utils.py index db6646e6916..72a29472748 100644 --- a/release/io/netrender/utils.py +++ b/release/io/netrender/utils.py @@ -22,9 +22,12 @@ def rnaOperator(rna_op): return rna_op def clientConnection(scene): - netrender = scene.network_render + netsettings = scene.network_render - conn = http.client.HTTPConnection(netrender.server_address, netrender.server_port) + if netsettings.server_address == "[default]": + bpy.ops.render.netclientscan() + + conn = http.client.HTTPConnection(netsettings.server_address, netsettings.server_port) if clientVerifyVersion(conn): return conn