netrender tab to spaces
This commit is contained in:
@@ -4,12 +4,12 @@
|
|||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
# as published by the Free Software Foundation; either version 2
|
# as published by the Free Software Foundation; either version 2
|
||||||
# of the License, or (at your option) any later version.
|
# of the License, or (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
# as published by the Free Software Foundation; either version 2
|
# as published by the Free Software Foundation; either version 2
|
||||||
# of the License, or (at your option) any later version.
|
# of the License, or (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
@@ -22,129 +22,129 @@ from netrender.utils import *
|
|||||||
import netrender.model
|
import netrender.model
|
||||||
|
|
||||||
class RatingRule:
|
class RatingRule:
|
||||||
def rate(self, job):
|
def rate(self, job):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
class ExclusionRule:
|
class ExclusionRule:
|
||||||
def test(self, job):
|
def test(self, job):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
class PriorityRule:
|
class PriorityRule:
|
||||||
def test(self, job):
|
def test(self, job):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
class Balancer:
|
class Balancer:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.rules = []
|
self.rules = []
|
||||||
self.priorities = []
|
self.priorities = []
|
||||||
self.exceptions = []
|
self.exceptions = []
|
||||||
|
|
||||||
def addRule(self, rule):
|
def addRule(self, rule):
|
||||||
self.rules.append(rule)
|
self.rules.append(rule)
|
||||||
|
|
||||||
def addPriority(self, priority):
|
def addPriority(self, priority):
|
||||||
self.priorities.append(priority)
|
self.priorities.append(priority)
|
||||||
|
|
||||||
def addException(self, exception):
|
def addException(self, exception):
|
||||||
self.exceptions.append(exception)
|
self.exceptions.append(exception)
|
||||||
|
|
||||||
def applyRules(self, job):
|
def applyRules(self, job):
|
||||||
return sum((rule.rate(job) for rule in self.rules))
|
return sum((rule.rate(job) for rule in self.rules))
|
||||||
|
|
||||||
def applyPriorities(self, job):
|
def applyPriorities(self, job):
|
||||||
for priority in self.priorities:
|
for priority in self.priorities:
|
||||||
if priority.test(job):
|
if priority.test(job):
|
||||||
return True # priorities are first
|
return True # priorities are first
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def applyExceptions(self, job):
|
def applyExceptions(self, job):
|
||||||
for exception in self.exceptions:
|
for exception in self.exceptions:
|
||||||
if exception.test(job):
|
if exception.test(job):
|
||||||
return True # exceptions are last
|
return True # exceptions are last
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def sortKey(self, job):
|
def sortKey(self, job):
|
||||||
return (1 if self.applyExceptions(job) else 0, # exceptions after
|
return (1 if self.applyExceptions(job) else 0, # exceptions after
|
||||||
0 if self.applyPriorities(job) else 1, # priorities first
|
0 if self.applyPriorities(job) else 1, # priorities first
|
||||||
self.applyRules(job))
|
self.applyRules(job))
|
||||||
|
|
||||||
def balance(self, jobs):
|
def balance(self, jobs):
|
||||||
if jobs:
|
if jobs:
|
||||||
# use inline copy to make sure the list is still accessible while sorting
|
# use inline copy to make sure the list is still accessible while sorting
|
||||||
jobs[:] = sorted(jobs, key=self.sortKey)
|
jobs[:] = sorted(jobs, key=self.sortKey)
|
||||||
return jobs[0]
|
return jobs[0]
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# ==========================
|
# ==========================
|
||||||
|
|
||||||
class RatingUsage(RatingRule):
|
class RatingUsage(RatingRule):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Usage rating"
|
return "Usage rating"
|
||||||
|
|
||||||
def rate(self, job):
|
def rate(self, job):
|
||||||
# less usage is better
|
# less usage is better
|
||||||
return job.usage / job.priority
|
return job.usage / job.priority
|
||||||
|
|
||||||
class RatingUsageByCategory(RatingRule):
|
class RatingUsageByCategory(RatingRule):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Usage per category rating"
|
return "Usage per category rating"
|
||||||
|
|
||||||
def __init__(self, get_jobs):
|
|
||||||
self.getJobs = get_jobs
|
|
||||||
def rate(self, job):
|
|
||||||
total_category_usage = sum([j.usage for j in self.getJobs() if j.category == job.category])
|
|
||||||
maximum_priority = max([j.priority for j in self.getJobs() if j.category == job.category])
|
|
||||||
|
|
||||||
# less usage is better
|
|
||||||
return total_category_usage / maximum_priority
|
|
||||||
|
|
||||||
class NewJobPriority(PriorityRule):
|
|
||||||
def str_limit(self):
|
|
||||||
return "less than %i frame%s done" % (self.limit, "s" if self.limit > 1 else "")
|
|
||||||
|
|
||||||
def __str__(self):
|
def __init__(self, get_jobs):
|
||||||
return "Priority to new jobs"
|
self.getJobs = get_jobs
|
||||||
|
def rate(self, job):
|
||||||
def __init__(self, limit = 1):
|
total_category_usage = sum([j.usage for j in self.getJobs() if j.category == job.category])
|
||||||
self.limit = limit
|
maximum_priority = max([j.priority for j in self.getJobs() if j.category == job.category])
|
||||||
|
|
||||||
def test(self, job):
|
# less usage is better
|
||||||
return job.countFrames(status = DONE) < self.limit
|
return total_category_usage / maximum_priority
|
||||||
|
|
||||||
|
class NewJobPriority(PriorityRule):
|
||||||
|
def str_limit(self):
|
||||||
|
return "less than %i frame%s done" % (self.limit, "s" if self.limit > 1 else "")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Priority to new jobs"
|
||||||
|
|
||||||
|
def __init__(self, limit = 1):
|
||||||
|
self.limit = limit
|
||||||
|
|
||||||
|
def test(self, job):
|
||||||
|
return job.countFrames(status = DONE) < self.limit
|
||||||
|
|
||||||
class MinimumTimeBetweenDispatchPriority(PriorityRule):
|
class MinimumTimeBetweenDispatchPriority(PriorityRule):
|
||||||
def str_limit(self):
|
def str_limit(self):
|
||||||
return "more than %i minute%s since last" % (self.limit, "s" if self.limit > 1 else "")
|
return "more than %i minute%s since last" % (self.limit, "s" if self.limit > 1 else "")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Priority to jobs that haven't been dispatched recently"
|
return "Priority to jobs that haven't been dispatched recently"
|
||||||
|
|
||||||
def __init__(self, limit = 10):
|
def __init__(self, limit = 10):
|
||||||
self.limit = limit
|
self.limit = limit
|
||||||
|
|
||||||
def test(self, job):
|
def test(self, job):
|
||||||
return job.countFrames(status = DISPATCHED) == 0 and (time.time() - job.last_dispatched) / 60 > self.limit
|
return job.countFrames(status = DISPATCHED) == 0 and (time.time() - job.last_dispatched) / 60 > self.limit
|
||||||
|
|
||||||
class ExcludeQueuedEmptyJob(ExclusionRule):
|
class ExcludeQueuedEmptyJob(ExclusionRule):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Exclude queued and empty jobs"
|
return "Exclude queued and empty jobs"
|
||||||
|
|
||||||
def test(self, job):
|
|
||||||
return job.status != JOB_QUEUED or job.countFrames(status = QUEUED) == 0
|
|
||||||
|
|
||||||
class ExcludeSlavesLimit(ExclusionRule):
|
|
||||||
def str_limit(self):
|
|
||||||
return "more than %.0f%% of all slaves" % (self.limit * 100)
|
|
||||||
|
|
||||||
def __str__(self):
|
def test(self, job):
|
||||||
return "Exclude jobs that would use too many slaves"
|
return job.status != JOB_QUEUED or job.countFrames(status = QUEUED) == 0
|
||||||
|
|
||||||
def __init__(self, count_jobs, count_slaves, limit = 0.75):
|
class ExcludeSlavesLimit(ExclusionRule):
|
||||||
self.count_jobs = count_jobs
|
def str_limit(self):
|
||||||
self.count_slaves = count_slaves
|
return "more than %.0f%% of all slaves" % (self.limit * 100)
|
||||||
self.limit = limit
|
|
||||||
|
def __str__(self):
|
||||||
def test(self, job):
|
return "Exclude jobs that would use too many slaves"
|
||||||
return not ( self.count_jobs() == 1 or self.count_slaves() <= 1 or float(job.countSlaves() + 1) / self.count_slaves() <= self.limit )
|
|
||||||
|
def __init__(self, count_jobs, count_slaves, limit = 0.75):
|
||||||
|
self.count_jobs = count_jobs
|
||||||
|
self.count_slaves = count_slaves
|
||||||
|
self.limit = limit
|
||||||
|
|
||||||
|
def test(self, job):
|
||||||
|
return not ( self.count_jobs() == 1 or self.count_slaves() <= 1 or float(job.countSlaves() + 1) / self.count_slaves() <= self.limit )
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
# as published by the Free Software Foundation; either version 2
|
# as published by the Free Software Foundation; either version 2
|
||||||
# of the License, or (at your option) any later version.
|
# of the License, or (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
@@ -28,253 +28,253 @@ import netrender.master as master
|
|||||||
from netrender.utils import *
|
from netrender.utils import *
|
||||||
|
|
||||||
def addFluidFiles(job, path):
|
def addFluidFiles(job, path):
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
pattern = re.compile("fluidsurface_(final|preview)_([0-9]+)\.(bobj|bvel)\.gz")
|
pattern = re.compile("fluidsurface_(final|preview)_([0-9]+)\.(bobj|bvel)\.gz")
|
||||||
|
|
||||||
for fluid_file in sorted(os.listdir(path)):
|
for fluid_file in sorted(os.listdir(path)):
|
||||||
match = pattern.match(fluid_file)
|
match = pattern.match(fluid_file)
|
||||||
|
|
||||||
if match:
|
if match:
|
||||||
# fluid frames starts at 0, which explains the +1
|
# fluid frames starts at 0, which explains the +1
|
||||||
# This is stupid
|
# This is stupid
|
||||||
current_frame = int(match.groups()[1]) + 1
|
current_frame = int(match.groups()[1]) + 1
|
||||||
job.addFile(path + fluid_file, current_frame, current_frame)
|
job.addFile(path + fluid_file, current_frame, current_frame)
|
||||||
|
|
||||||
def addPointCache(job, ob, point_cache, default_path):
|
def addPointCache(job, ob, point_cache, default_path):
|
||||||
if not point_cache.disk_cache:
|
if not point_cache.disk_cache:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
name = point_cache.name
|
name = point_cache.name
|
||||||
if name == "":
|
if name == "":
|
||||||
name = "".join(["%02X" % ord(c) for c in ob.name])
|
name = "".join(["%02X" % ord(c) for c in ob.name])
|
||||||
|
|
||||||
cache_path = bpy.utils.expandpath(point_cache.filepath) if point_cache.external else default_path
|
cache_path = bpy.utils.expandpath(point_cache.filepath) if point_cache.external else default_path
|
||||||
|
|
||||||
index = "%02i" % point_cache.index
|
index = "%02i" % point_cache.index
|
||||||
|
|
||||||
if os.path.exists(cache_path):
|
if os.path.exists(cache_path):
|
||||||
pattern = re.compile(name + "_([0-9]+)_" + index + "\.bphys")
|
pattern = re.compile(name + "_([0-9]+)_" + index + "\.bphys")
|
||||||
|
|
||||||
cache_files = []
|
cache_files = []
|
||||||
|
|
||||||
|
for cache_file in sorted(os.listdir(cache_path)):
|
||||||
|
match = pattern.match(cache_file)
|
||||||
|
|
||||||
|
if match:
|
||||||
|
cache_frame = int(match.groups()[0])
|
||||||
|
cache_files.append((cache_frame, cache_file))
|
||||||
|
|
||||||
|
cache_files.sort()
|
||||||
|
|
||||||
|
if len(cache_files) == 1:
|
||||||
|
cache_frame, cache_file = cache_files[0]
|
||||||
|
job.addFile(cache_path + cache_file, cache_frame, cache_frame)
|
||||||
|
else:
|
||||||
|
for i in range(len(cache_files)):
|
||||||
|
current_item = cache_files[i]
|
||||||
|
next_item = cache_files[i+1] if i + 1 < len(cache_files) else None
|
||||||
|
previous_item = cache_files[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)
|
||||||
|
|
||||||
for cache_file in sorted(os.listdir(cache_path)):
|
|
||||||
match = pattern.match(cache_file)
|
|
||||||
|
|
||||||
if match:
|
|
||||||
cache_frame = int(match.groups()[0])
|
|
||||||
cache_files.append((cache_frame, cache_file))
|
|
||||||
|
|
||||||
cache_files.sort()
|
|
||||||
|
|
||||||
if len(cache_files) == 1:
|
|
||||||
cache_frame, cache_file = cache_files[0]
|
|
||||||
job.addFile(cache_path + cache_file, cache_frame, cache_frame)
|
|
||||||
else:
|
|
||||||
for i in range(len(cache_files)):
|
|
||||||
current_item = cache_files[i]
|
|
||||||
next_item = cache_files[i+1] if i + 1 < len(cache_files) else None
|
|
||||||
previous_item = cache_files[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)
|
|
||||||
|
|
||||||
def clientSendJob(conn, scene, anim = False):
|
def clientSendJob(conn, scene, anim = False):
|
||||||
netsettings = scene.network_render
|
netsettings = scene.network_render
|
||||||
job = netrender.model.RenderJob()
|
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:
|
|
||||||
job.addFile(bpy.utils.expandpath(lib.filename))
|
|
||||||
|
|
||||||
###########################
|
|
||||||
# IMAGES
|
|
||||||
###########################
|
|
||||||
for image in bpy.data.images:
|
|
||||||
if image.source == "FILE" and not image.packed_file:
|
|
||||||
job.addFile(bpy.utils.expandpath(image.filename))
|
|
||||||
|
|
||||||
###########################
|
|
||||||
# FLUID + POINT CACHE
|
|
||||||
###########################
|
|
||||||
root, ext = os.path.splitext(name)
|
|
||||||
default_path = path + os.sep + "blendcache_" + root + os.sep # need an API call for that
|
|
||||||
|
|
||||||
for object in bpy.data.objects:
|
if anim:
|
||||||
for modifier in object.modifiers:
|
for f in range(scene.start_frame, scene.end_frame + 1):
|
||||||
if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
|
job.addFrame(f)
|
||||||
addFluidFiles(job, bpy.utils.expandpath(modifier.settings.path))
|
else:
|
||||||
elif modifier.type == "CLOTH":
|
job.addFrame(scene.current_frame)
|
||||||
addPointCache(job, object, modifier.point_cache, default_path)
|
|
||||||
elif modifier.type == "SOFT_BODY":
|
|
||||||
addPointCache(job, object, modifier.point_cache, default_path)
|
|
||||||
elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN":
|
|
||||||
addPointCache(job, object, modifier.domain_settings.point_cache_low, default_path)
|
|
||||||
if modifier.domain_settings.highres:
|
|
||||||
addPointCache(job, object, modifier.domain_settings.point_cache_high, default_path)
|
|
||||||
|
|
||||||
# particles modifier are stupid and don't contain data
|
filename = bpy.data.filename
|
||||||
# we have to go through the object property
|
job.addFile(filename)
|
||||||
for psys in object.particle_systems:
|
|
||||||
addPointCache(job, object, psys.point_cache, default_path)
|
job_name = netsettings.job_name
|
||||||
|
path, name = os.path.split(filename)
|
||||||
#print(job.files)
|
if job_name == "[default]":
|
||||||
|
job_name = name
|
||||||
job.name = job_name
|
|
||||||
job.category = netsettings.job_category
|
###########################
|
||||||
|
# LIBRARIES
|
||||||
for slave in netrender.blacklist:
|
###########################
|
||||||
job.blacklist.append(slave.id)
|
for lib in bpy.data.libraries:
|
||||||
|
job.addFile(bpy.utils.expandpath(lib.filename))
|
||||||
job.chunks = netsettings.chunks
|
|
||||||
job.priority = netsettings.priority
|
###########################
|
||||||
|
# IMAGES
|
||||||
# try to send path first
|
###########################
|
||||||
conn.request("POST", "/job", repr(job.serialize()))
|
for image in bpy.data.images:
|
||||||
response = conn.getresponse()
|
if image.source == "FILE" and not image.packed_file:
|
||||||
|
job.addFile(bpy.utils.expandpath(image.filename))
|
||||||
job_id = response.getheader("job-id")
|
|
||||||
|
###########################
|
||||||
# if not ACCEPTED (but not processed), send files
|
# FLUID + POINT CACHE
|
||||||
if response.status == http.client.ACCEPTED:
|
###########################
|
||||||
for rfile in job.files:
|
root, ext = os.path.splitext(name)
|
||||||
f = open(rfile.filepath, "rb")
|
default_path = path + os.sep + "blendcache_" + root + os.sep # need an API call for that
|
||||||
conn.request("PUT", fileURL(job_id, rfile.index), f)
|
|
||||||
f.close()
|
for object in bpy.data.objects:
|
||||||
response = conn.getresponse()
|
for modifier in object.modifiers:
|
||||||
|
if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
|
||||||
# server will reply with ACCEPTED until all files are found
|
addFluidFiles(job, bpy.utils.expandpath(modifier.settings.path))
|
||||||
|
elif modifier.type == "CLOTH":
|
||||||
return job_id
|
addPointCache(job, object, modifier.point_cache, default_path)
|
||||||
|
elif modifier.type == "SOFT_BODY":
|
||||||
|
addPointCache(job, object, modifier.point_cache, default_path)
|
||||||
|
elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN":
|
||||||
|
addPointCache(job, object, modifier.domain_settings.point_cache_low, default_path)
|
||||||
|
if modifier.domain_settings.highres:
|
||||||
|
addPointCache(job, object, modifier.domain_settings.point_cache_high, default_path)
|
||||||
|
|
||||||
|
# particles modifier are stupid and don't contain data
|
||||||
|
# we have to go through the object property
|
||||||
|
for psys in object.particle_systems:
|
||||||
|
addPointCache(job, object, psys.point_cache, default_path)
|
||||||
|
|
||||||
|
#print(job.files)
|
||||||
|
|
||||||
|
job.name = job_name
|
||||||
|
job.category = netsettings.job_category
|
||||||
|
|
||||||
|
for slave in netrender.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 rfile in job.files:
|
||||||
|
f = open(rfile.filepath, "rb")
|
||||||
|
conn.request("PUT", fileURL(job_id, rfile.index), f)
|
||||||
|
f.close()
|
||||||
|
response = conn.getresponse()
|
||||||
|
|
||||||
|
# server will reply with ACCEPTED until all files are found
|
||||||
|
|
||||||
|
return job_id
|
||||||
|
|
||||||
def requestResult(conn, job_id, frame):
|
def requestResult(conn, job_id, frame):
|
||||||
conn.request("GET", renderURL(job_id, frame))
|
conn.request("GET", renderURL(job_id, frame))
|
||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class NetworkRenderEngine(bpy.types.RenderEngine):
|
class NetworkRenderEngine(bpy.types.RenderEngine):
|
||||||
bl_idname = 'NET_RENDER'
|
bl_idname = 'NET_RENDER'
|
||||||
bl_label = "Network Render"
|
bl_label = "Network Render"
|
||||||
def render(self, scene):
|
def render(self, scene):
|
||||||
if scene.network_render.mode == "RENDER_CLIENT":
|
if scene.network_render.mode == "RENDER_CLIENT":
|
||||||
self.render_client(scene)
|
self.render_client(scene)
|
||||||
elif scene.network_render.mode == "RENDER_SLAVE":
|
elif scene.network_render.mode == "RENDER_SLAVE":
|
||||||
self.render_slave(scene)
|
self.render_slave(scene)
|
||||||
elif scene.network_render.mode == "RENDER_MASTER":
|
elif scene.network_render.mode == "RENDER_MASTER":
|
||||||
self.render_master(scene)
|
self.render_master(scene)
|
||||||
else:
|
else:
|
||||||
print("UNKNOWN OPERATION MODE")
|
print("UNKNOWN OPERATION MODE")
|
||||||
|
|
||||||
def render_master(self, scene):
|
def render_master(self, scene):
|
||||||
netsettings = scene.network_render
|
netsettings = scene.network_render
|
||||||
|
|
||||||
address = "" if netsettings.server_address == "[default]" else netsettings.server_address
|
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)
|
master.runMaster((address, netsettings.server_port), netsettings.server_broadcast, netsettings.path, self.update_stats, self.test_break)
|
||||||
|
|
||||||
|
|
||||||
def render_slave(self, scene):
|
def render_slave(self, scene):
|
||||||
slave.render_slave(self, scene.network_render)
|
slave.render_slave(self, scene.network_render)
|
||||||
|
|
||||||
def render_client(self, scene):
|
def render_client(self, scene):
|
||||||
netsettings = scene.network_render
|
netsettings = scene.network_render
|
||||||
self.update_stats("", "Network render client initiation")
|
self.update_stats("", "Network render client initiation")
|
||||||
|
|
||||||
|
|
||||||
conn = clientConnection(netsettings.server_address, netsettings.server_port)
|
conn = clientConnection(netsettings.server_address, netsettings.server_port)
|
||||||
|
|
||||||
if conn:
|
if conn:
|
||||||
# Sending file
|
# Sending file
|
||||||
|
|
||||||
self.update_stats("", "Network render exporting")
|
self.update_stats("", "Network render exporting")
|
||||||
|
|
||||||
new_job = False
|
new_job = False
|
||||||
|
|
||||||
job_id = netsettings.job_id
|
job_id = netsettings.job_id
|
||||||
|
|
||||||
# reading back result
|
# reading back result
|
||||||
|
|
||||||
self.update_stats("", "Network render waiting for results")
|
self.update_stats("", "Network render waiting for results")
|
||||||
|
|
||||||
requestResult(conn, job_id, scene.current_frame)
|
requestResult(conn, job_id, scene.current_frame)
|
||||||
response = conn.getresponse()
|
response = conn.getresponse()
|
||||||
|
|
||||||
if response.status == http.client.NO_CONTENT:
|
if response.status == http.client.NO_CONTENT:
|
||||||
new_job = True
|
new_job = True
|
||||||
netsettings.job_id = clientSendJob(conn, scene)
|
netsettings.job_id = clientSendJob(conn, scene)
|
||||||
job_id = netsettings.job_id
|
job_id = netsettings.job_id
|
||||||
|
|
||||||
requestResult(conn, job_id, scene.current_frame)
|
requestResult(conn, job_id, scene.current_frame)
|
||||||
response = conn.getresponse()
|
response = conn.getresponse()
|
||||||
|
|
||||||
while response.status == http.client.ACCEPTED and not self.test_break():
|
while response.status == http.client.ACCEPTED and not self.test_break():
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
requestResult(conn, job_id, scene.current_frame)
|
requestResult(conn, job_id, scene.current_frame)
|
||||||
response = conn.getresponse()
|
response = conn.getresponse()
|
||||||
|
|
||||||
# cancel new jobs (animate on network) on break
|
# cancel new jobs (animate on network) on break
|
||||||
if self.test_break() and new_job:
|
if self.test_break() and new_job:
|
||||||
conn.request("POST", cancelURL(job_id))
|
conn.request("POST", cancelURL(job_id))
|
||||||
response = conn.getresponse()
|
response = conn.getresponse()
|
||||||
print( response.status, response.reason )
|
print( response.status, response.reason )
|
||||||
netsettings.job_id = 0
|
netsettings.job_id = 0
|
||||||
|
|
||||||
if response.status != http.client.OK:
|
if response.status != http.client.OK:
|
||||||
conn.close()
|
conn.close()
|
||||||
return
|
return
|
||||||
|
|
||||||
r = scene.render_data
|
r = scene.render_data
|
||||||
x= int(r.resolution_x*r.resolution_percentage*0.01)
|
x= int(r.resolution_x*r.resolution_percentage*0.01)
|
||||||
y= int(r.resolution_y*r.resolution_percentage*0.01)
|
y= int(r.resolution_y*r.resolution_percentage*0.01)
|
||||||
|
|
||||||
f = open(netsettings.path + "output.exr", "wb")
|
f = open(netsettings.path + "output.exr", "wb")
|
||||||
buf = response.read(1024)
|
buf = response.read(1024)
|
||||||
|
|
||||||
while buf:
|
while buf:
|
||||||
f.write(buf)
|
f.write(buf)
|
||||||
buf = response.read(1024)
|
buf = response.read(1024)
|
||||||
|
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
result = self.begin_result(0, 0, x, y)
|
result = self.begin_result(0, 0, x, y)
|
||||||
result.load_from_file(netsettings.path + "output.exr", 0, 0)
|
result.load_from_file(netsettings.path + "output.exr", 0, 0)
|
||||||
self.end_result(result)
|
self.end_result(result)
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
def compatible(module):
|
def compatible(module):
|
||||||
module = __import__(module)
|
module = __import__(module)
|
||||||
for subclass in module.__dict__.values():
|
for subclass in module.__dict__.values():
|
||||||
try: subclass.COMPAT_ENGINES.add('NET_RENDER')
|
try: subclass.COMPAT_ENGINES.add('NET_RENDER')
|
||||||
except: pass
|
except: pass
|
||||||
del module
|
del module
|
||||||
|
|
||||||
compatible("properties_render")
|
compatible("properties_render")
|
||||||
compatible("properties_world")
|
compatible("properties_world")
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -4,12 +4,12 @@
|
|||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
# as published by the Free Software Foundation; either version 2
|
# as published by the Free Software Foundation; either version 2
|
||||||
# of the License, or (at your option) any later version.
|
# of the License, or (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
@@ -24,226 +24,226 @@ from netrender.utils import *
|
|||||||
src_folder = os.path.split(__file__)[0]
|
src_folder = os.path.split(__file__)[0]
|
||||||
|
|
||||||
def get(handler):
|
def get(handler):
|
||||||
def output(text):
|
def output(text):
|
||||||
handler.wfile.write(bytes(text, encoding='utf8'))
|
handler.wfile.write(bytes(text, encoding='utf8'))
|
||||||
|
|
||||||
def head(title):
|
def head(title):
|
||||||
output("<html><head>")
|
output("<html><head>")
|
||||||
output("<script src='/html/netrender.js' type='text/javascript'></script>")
|
output("<script src='/html/netrender.js' type='text/javascript'></script>")
|
||||||
# output("<script src='/html/json2.js' type='text/javascript'></script>")
|
# output("<script src='/html/json2.js' type='text/javascript'></script>")
|
||||||
output("<title>")
|
output("<title>")
|
||||||
output(title)
|
output(title)
|
||||||
output("</title></head><body>")
|
output("</title></head><body>")
|
||||||
output("<link rel='stylesheet' href='/html/netrender.css' type='text/css'>")
|
output("<link rel='stylesheet' href='/html/netrender.css' type='text/css'>")
|
||||||
|
|
||||||
|
|
||||||
def link(text, url):
|
|
||||||
return "<a href='%s'>%s</a>" % (url, text)
|
|
||||||
|
|
||||||
def startTable(border=1, class_style = None, caption = None):
|
|
||||||
output("<table border='%i'" % border)
|
|
||||||
|
|
||||||
if class_style:
|
|
||||||
output(" class='%s'" % class_style)
|
|
||||||
|
|
||||||
output(">")
|
|
||||||
|
|
||||||
if caption:
|
|
||||||
output("<caption>%s</caption>" % caption)
|
|
||||||
|
|
||||||
def headerTable(*headers):
|
|
||||||
output("<thead><tr>")
|
|
||||||
|
|
||||||
for c in headers:
|
|
||||||
output("<td>" + c + "</td>")
|
|
||||||
|
|
||||||
output("</tr></thead>")
|
|
||||||
|
|
||||||
def rowTable(*data, id = None, class_style = None, extra = None):
|
|
||||||
output("<tr")
|
|
||||||
|
|
||||||
if id:
|
|
||||||
output(" id='%s'" % id)
|
|
||||||
|
|
||||||
if class_style:
|
|
||||||
output(" class='%s'" % class_style)
|
|
||||||
|
|
||||||
if extra:
|
def link(text, url):
|
||||||
output(" %s" % extra)
|
return "<a href='%s'>%s</a>" % (url, text)
|
||||||
|
|
||||||
output(">")
|
def startTable(border=1, class_style = None, caption = None):
|
||||||
|
output("<table border='%i'" % border)
|
||||||
for c in data:
|
|
||||||
output("<td>" + str(c) + "</td>")
|
|
||||||
|
|
||||||
output("</tr>")
|
|
||||||
|
|
||||||
def endTable():
|
|
||||||
output("</table>")
|
|
||||||
|
|
||||||
if handler.path == "/html/netrender.js":
|
|
||||||
f = open(os.path.join(src_folder, "netrender.js"), 'rb')
|
|
||||||
|
|
||||||
handler.send_head(content = "text/javascript")
|
|
||||||
shutil.copyfileobj(f, handler.wfile)
|
|
||||||
|
|
||||||
f.close()
|
|
||||||
elif handler.path == "/html/netrender.css":
|
|
||||||
f = open(os.path.join(src_folder, "netrender.css"), 'rb')
|
|
||||||
|
|
||||||
handler.send_head(content = "text/css")
|
|
||||||
shutil.copyfileobj(f, handler.wfile)
|
|
||||||
|
|
||||||
f.close()
|
|
||||||
elif handler.path == "/html" or handler.path == "/":
|
|
||||||
handler.send_head(content = "text/html")
|
|
||||||
head("NetRender")
|
|
||||||
|
|
||||||
output("<h2>Master</h2>")
|
|
||||||
|
|
||||||
output("""<button title="remove all jobs" onclick="request('/clear', null);">CLEAR JOB LIST</button>""")
|
|
||||||
|
|
||||||
startTable(caption = "Rules", class_style = "rules")
|
if class_style:
|
||||||
|
output(" class='%s'" % class_style)
|
||||||
|
|
||||||
headerTable("type", "description", "limit")
|
output(">")
|
||||||
|
|
||||||
for rule in handler.server.balancer.rules:
|
if caption:
|
||||||
rowTable("rating", rule, rule.str_limit() if hasattr(rule, "limit") else " ")
|
output("<caption>%s</caption>" % caption)
|
||||||
|
|
||||||
for rule in handler.server.balancer.priorities:
|
def headerTable(*headers):
|
||||||
rowTable("priority", rule, rule.str_limit() if hasattr(rule, "limit") else " ")
|
output("<thead><tr>")
|
||||||
|
|
||||||
for rule in handler.server.balancer.exceptions:
|
|
||||||
rowTable("exception", rule, rule.str_limit() if hasattr(rule, "limit") else " ")
|
|
||||||
|
|
||||||
endTable()
|
for c in headers:
|
||||||
|
output("<td>" + c + "</td>")
|
||||||
|
|
||||||
output("<h2>Slaves</h2>")
|
output("</tr></thead>")
|
||||||
|
|
||||||
startTable()
|
|
||||||
headerTable("name", "address", "last seen", "stats", "job")
|
|
||||||
|
|
||||||
for slave in handler.server.slaves:
|
|
||||||
rowTable(slave.name, slave.address[0], time.ctime(slave.last_seen), slave.stats, link(slave.job.name, "/html/job" + slave.job.id) if slave.job else "None")
|
|
||||||
|
|
||||||
endTable()
|
|
||||||
|
|
||||||
output("<h2>Jobs</h2>")
|
|
||||||
|
|
||||||
startTable()
|
|
||||||
headerTable(
|
|
||||||
" ",
|
|
||||||
"id",
|
|
||||||
"name",
|
|
||||||
"category",
|
|
||||||
"chunks",
|
|
||||||
"priority",
|
|
||||||
"usage",
|
|
||||||
"wait",
|
|
||||||
"status",
|
|
||||||
"length",
|
|
||||||
"done",
|
|
||||||
"dispatched",
|
|
||||||
"error",
|
|
||||||
"first",
|
|
||||||
"exception"
|
|
||||||
)
|
|
||||||
|
|
||||||
handler.server.balance()
|
def rowTable(*data, id = None, class_style = None, extra = None):
|
||||||
|
output("<tr")
|
||||||
for job in handler.server.jobs:
|
|
||||||
results = job.framesStatus()
|
|
||||||
rowTable(
|
|
||||||
"""<button title="cancel job" onclick="request('/cancel_%s', null);">X</button>""" % job.id +
|
|
||||||
"""<button title="reset all frames" onclick="request('/resetall_%s_0', null);">R</button>""" % job.id,
|
|
||||||
job.id,
|
|
||||||
link(job.name, "/html/job" + job.id),
|
|
||||||
job.category if job.category else "<i>None</i>",
|
|
||||||
str(job.chunks) +
|
|
||||||
"""<button title="increase priority" onclick="request('/edit_%s', "{'chunks': %i}");">+</button>""" % (job.id, job.chunks + 1) +
|
|
||||||
"""<button title="decrease priority" onclick="request('/edit_%s', "{'chunks': %i}");" %s>-</button>""" % (job.id, job.chunks - 1, "disabled=True" if job.chunks == 1 else ""),
|
|
||||||
str(job.priority) +
|
|
||||||
"""<button title="increase chunks size" onclick="request('/edit_%s', "{'priority': %i}");">+</button>""" % (job.id, job.priority + 1) +
|
|
||||||
"""<button title="decrease chunks size" onclick="request('/edit_%s', "{'priority': %i}");" %s>-</button>""" % (job.id, job.priority - 1, "disabled=True" if job.priority == 1 else ""),
|
|
||||||
"%0.1f%%" % (job.usage * 100),
|
|
||||||
"%is" % int(time.time() - job.last_dispatched),
|
|
||||||
job.statusText(),
|
|
||||||
len(job),
|
|
||||||
results[DONE],
|
|
||||||
results[DISPATCHED],
|
|
||||||
str(results[ERROR]) +
|
|
||||||
"""<button title="reset error frames" onclick="request('/reset_%s_0', null);" %s>R</button>""" % (job.id, "disabled=True" if not results[ERROR] else ""),
|
|
||||||
handler.server.balancer.applyPriorities(job), handler.server.balancer.applyExceptions(job)
|
|
||||||
)
|
|
||||||
|
|
||||||
endTable()
|
|
||||||
|
|
||||||
output("</body></html>")
|
|
||||||
|
|
||||||
elif handler.path.startswith("/html/job"):
|
|
||||||
handler.send_head(content = "text/html")
|
|
||||||
job_id = handler.path[9:]
|
|
||||||
|
|
||||||
head("NetRender")
|
|
||||||
|
|
||||||
job = handler.server.getJobID(job_id)
|
|
||||||
|
|
||||||
if job:
|
|
||||||
output("<h2>Files</h2>")
|
|
||||||
|
|
||||||
startTable()
|
|
||||||
headerTable("path")
|
|
||||||
|
|
||||||
tot_cache = 0
|
|
||||||
tot_fluid = 0
|
|
||||||
|
|
||||||
for file in job.files:
|
|
||||||
if file.filepath.endswith(".bphys"):
|
|
||||||
tot_cache += 1
|
|
||||||
elif file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
|
|
||||||
tot_fluid += 1
|
|
||||||
else:
|
|
||||||
rowTable(file.filepath)
|
|
||||||
|
|
||||||
if tot_cache > 0:
|
if id:
|
||||||
rowTable("%i physic cache files" % tot_cache, class_style = "toggle", extra = "onclick='toggleDisplay(".cache", "none", "table-row")'")
|
output(" id='%s'" % id)
|
||||||
for file in job.files:
|
|
||||||
if file.filepath.endswith(".bphys"):
|
|
||||||
rowTable(os.path.split(file.filepath)[1], class_style = "cache")
|
|
||||||
|
|
||||||
if tot_fluid > 0:
|
|
||||||
rowTable("%i fluid bake files" % tot_fluid, class_style = "toggle", extra = "onclick='toggleDisplay(".fluid", "none", "table-row")'")
|
|
||||||
for file in job.files:
|
|
||||||
if file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
|
|
||||||
rowTable(os.path.split(file.filepath)[1], class_style = "fluid")
|
|
||||||
|
|
||||||
endTable()
|
if class_style:
|
||||||
|
output(" class='%s'" % class_style)
|
||||||
output("<h2>Blacklist</h2>")
|
|
||||||
|
|
||||||
if job.blacklist:
|
|
||||||
startTable()
|
|
||||||
headerTable("name", "address")
|
|
||||||
|
|
||||||
for slave_id in job.blacklist:
|
|
||||||
slave = handler.server.slaves_map[slave_id]
|
|
||||||
rowTable(slave.name, slave.address[0])
|
|
||||||
|
|
||||||
endTable()
|
|
||||||
else:
|
|
||||||
output("<i>Empty</i>")
|
|
||||||
|
|
||||||
output("<h2>Frames</h2>")
|
if extra:
|
||||||
|
output(" %s" % extra)
|
||||||
startTable()
|
|
||||||
headerTable("no", "status", "render time", "slave", "log", "result")
|
output(">")
|
||||||
|
|
||||||
for frame in job.frames:
|
for c in data:
|
||||||
rowTable(frame.number, frame.statusText(), "%.1fs" % frame.time, frame.slave.name if frame.slave else " ", link("view log", logURL(job_id, frame.number)) if frame.log_path else " ", link("view result", renderURL(job_id, frame.number)) if frame.status == DONE else " ")
|
output("<td>" + str(c) + "</td>")
|
||||||
|
|
||||||
endTable()
|
output("</tr>")
|
||||||
else:
|
|
||||||
output("no such job")
|
def endTable():
|
||||||
|
output("</table>")
|
||||||
output("</body></html>")
|
|
||||||
|
if handler.path == "/html/netrender.js":
|
||||||
|
f = open(os.path.join(src_folder, "netrender.js"), 'rb')
|
||||||
|
|
||||||
|
handler.send_head(content = "text/javascript")
|
||||||
|
shutil.copyfileobj(f, handler.wfile)
|
||||||
|
|
||||||
|
f.close()
|
||||||
|
elif handler.path == "/html/netrender.css":
|
||||||
|
f = open(os.path.join(src_folder, "netrender.css"), 'rb')
|
||||||
|
|
||||||
|
handler.send_head(content = "text/css")
|
||||||
|
shutil.copyfileobj(f, handler.wfile)
|
||||||
|
|
||||||
|
f.close()
|
||||||
|
elif handler.path == "/html" or handler.path == "/":
|
||||||
|
handler.send_head(content = "text/html")
|
||||||
|
head("NetRender")
|
||||||
|
|
||||||
|
output("<h2>Master</h2>")
|
||||||
|
|
||||||
|
output("""<button title="remove all jobs" onclick="request('/clear', null);">CLEAR JOB LIST</button>""")
|
||||||
|
|
||||||
|
startTable(caption = "Rules", class_style = "rules")
|
||||||
|
|
||||||
|
headerTable("type", "description", "limit")
|
||||||
|
|
||||||
|
for rule in handler.server.balancer.rules:
|
||||||
|
rowTable("rating", rule, rule.str_limit() if hasattr(rule, "limit") else " ")
|
||||||
|
|
||||||
|
for rule in handler.server.balancer.priorities:
|
||||||
|
rowTable("priority", rule, rule.str_limit() if hasattr(rule, "limit") else " ")
|
||||||
|
|
||||||
|
for rule in handler.server.balancer.exceptions:
|
||||||
|
rowTable("exception", rule, rule.str_limit() if hasattr(rule, "limit") else " ")
|
||||||
|
|
||||||
|
endTable()
|
||||||
|
|
||||||
|
output("<h2>Slaves</h2>")
|
||||||
|
|
||||||
|
startTable()
|
||||||
|
headerTable("name", "address", "last seen", "stats", "job")
|
||||||
|
|
||||||
|
for slave in handler.server.slaves:
|
||||||
|
rowTable(slave.name, slave.address[0], time.ctime(slave.last_seen), slave.stats, link(slave.job.name, "/html/job" + slave.job.id) if slave.job else "None")
|
||||||
|
|
||||||
|
endTable()
|
||||||
|
|
||||||
|
output("<h2>Jobs</h2>")
|
||||||
|
|
||||||
|
startTable()
|
||||||
|
headerTable(
|
||||||
|
" ",
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"category",
|
||||||
|
"chunks",
|
||||||
|
"priority",
|
||||||
|
"usage",
|
||||||
|
"wait",
|
||||||
|
"status",
|
||||||
|
"length",
|
||||||
|
"done",
|
||||||
|
"dispatched",
|
||||||
|
"error",
|
||||||
|
"first",
|
||||||
|
"exception"
|
||||||
|
)
|
||||||
|
|
||||||
|
handler.server.balance()
|
||||||
|
|
||||||
|
for job in handler.server.jobs:
|
||||||
|
results = job.framesStatus()
|
||||||
|
rowTable(
|
||||||
|
"""<button title="cancel job" onclick="request('/cancel_%s', null);">X</button>""" % job.id +
|
||||||
|
"""<button title="reset all frames" onclick="request('/resetall_%s_0', null);">R</button>""" % job.id,
|
||||||
|
job.id,
|
||||||
|
link(job.name, "/html/job" + job.id),
|
||||||
|
job.category if job.category else "<i>None</i>",
|
||||||
|
str(job.chunks) +
|
||||||
|
"""<button title="increase priority" onclick="request('/edit_%s', "{'chunks': %i}");">+</button>""" % (job.id, job.chunks + 1) +
|
||||||
|
"""<button title="decrease priority" onclick="request('/edit_%s', "{'chunks': %i}");" %s>-</button>""" % (job.id, job.chunks - 1, "disabled=True" if job.chunks == 1 else ""),
|
||||||
|
str(job.priority) +
|
||||||
|
"""<button title="increase chunks size" onclick="request('/edit_%s', "{'priority': %i}");">+</button>""" % (job.id, job.priority + 1) +
|
||||||
|
"""<button title="decrease chunks size" onclick="request('/edit_%s', "{'priority': %i}");" %s>-</button>""" % (job.id, job.priority - 1, "disabled=True" if job.priority == 1 else ""),
|
||||||
|
"%0.1f%%" % (job.usage * 100),
|
||||||
|
"%is" % int(time.time() - job.last_dispatched),
|
||||||
|
job.statusText(),
|
||||||
|
len(job),
|
||||||
|
results[DONE],
|
||||||
|
results[DISPATCHED],
|
||||||
|
str(results[ERROR]) +
|
||||||
|
"""<button title="reset error frames" onclick="request('/reset_%s_0', null);" %s>R</button>""" % (job.id, "disabled=True" if not results[ERROR] else ""),
|
||||||
|
handler.server.balancer.applyPriorities(job), handler.server.balancer.applyExceptions(job)
|
||||||
|
)
|
||||||
|
|
||||||
|
endTable()
|
||||||
|
|
||||||
|
output("</body></html>")
|
||||||
|
|
||||||
|
elif handler.path.startswith("/html/job"):
|
||||||
|
handler.send_head(content = "text/html")
|
||||||
|
job_id = handler.path[9:]
|
||||||
|
|
||||||
|
head("NetRender")
|
||||||
|
|
||||||
|
job = handler.server.getJobID(job_id)
|
||||||
|
|
||||||
|
if job:
|
||||||
|
output("<h2>Files</h2>")
|
||||||
|
|
||||||
|
startTable()
|
||||||
|
headerTable("path")
|
||||||
|
|
||||||
|
tot_cache = 0
|
||||||
|
tot_fluid = 0
|
||||||
|
|
||||||
|
for file in job.files:
|
||||||
|
if file.filepath.endswith(".bphys"):
|
||||||
|
tot_cache += 1
|
||||||
|
elif file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
|
||||||
|
tot_fluid += 1
|
||||||
|
else:
|
||||||
|
rowTable(file.filepath)
|
||||||
|
|
||||||
|
if tot_cache > 0:
|
||||||
|
rowTable("%i physic cache files" % tot_cache, class_style = "toggle", extra = "onclick='toggleDisplay(".cache", "none", "table-row")'")
|
||||||
|
for file in job.files:
|
||||||
|
if file.filepath.endswith(".bphys"):
|
||||||
|
rowTable(os.path.split(file.filepath)[1], class_style = "cache")
|
||||||
|
|
||||||
|
if tot_fluid > 0:
|
||||||
|
rowTable("%i fluid bake files" % tot_fluid, class_style = "toggle", extra = "onclick='toggleDisplay(".fluid", "none", "table-row")'")
|
||||||
|
for file in job.files:
|
||||||
|
if file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
|
||||||
|
rowTable(os.path.split(file.filepath)[1], class_style = "fluid")
|
||||||
|
|
||||||
|
endTable()
|
||||||
|
|
||||||
|
output("<h2>Blacklist</h2>")
|
||||||
|
|
||||||
|
if job.blacklist:
|
||||||
|
startTable()
|
||||||
|
headerTable("name", "address")
|
||||||
|
|
||||||
|
for slave_id in job.blacklist:
|
||||||
|
slave = handler.server.slaves_map[slave_id]
|
||||||
|
rowTable(slave.name, slave.address[0])
|
||||||
|
|
||||||
|
endTable()
|
||||||
|
else:
|
||||||
|
output("<i>Empty</i>")
|
||||||
|
|
||||||
|
output("<h2>Frames</h2>")
|
||||||
|
|
||||||
|
startTable()
|
||||||
|
headerTable("no", "status", "render time", "slave", "log", "result")
|
||||||
|
|
||||||
|
for frame in job.frames:
|
||||||
|
rowTable(frame.number, frame.statusText(), "%.1fs" % frame.time, frame.slave.name if frame.slave else " ", link("view log", logURL(job_id, frame.number)) if frame.log_path else " ", link("view result", renderURL(job_id, frame.number)) if frame.status == DONE else " ")
|
||||||
|
|
||||||
|
endTable()
|
||||||
|
else:
|
||||||
|
output("no such job")
|
||||||
|
|
||||||
|
output("</body></html>")
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
# as published by the Free Software Foundation; either version 2
|
# as published by the Free Software Foundation; either version 2
|
||||||
# of the License, or (at your option) any later version.
|
# of the License, or (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
@@ -23,256 +23,256 @@ import subprocess, shutil, time, hashlib
|
|||||||
from netrender.utils import *
|
from netrender.utils import *
|
||||||
|
|
||||||
class LogFile:
|
class LogFile:
|
||||||
def __init__(self, job_id = 0, slave_id = 0, frames = []):
|
def __init__(self, job_id = 0, slave_id = 0, frames = []):
|
||||||
self.job_id = job_id
|
self.job_id = job_id
|
||||||
self.slave_id = slave_id
|
self.slave_id = slave_id
|
||||||
self.frames = frames
|
self.frames = frames
|
||||||
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
return {
|
return {
|
||||||
"job_id": self.job_id,
|
"job_id": self.job_id,
|
||||||
"slave_id": self.slave_id,
|
"slave_id": self.slave_id,
|
||||||
"frames": self.frames
|
"frames": self.frames
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def materialize(data):
|
def materialize(data):
|
||||||
if not data:
|
if not data:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
logfile = LogFile()
|
logfile = LogFile()
|
||||||
logfile.job_id = data["job_id"]
|
logfile.job_id = data["job_id"]
|
||||||
logfile.slave_id = data["slave_id"]
|
logfile.slave_id = data["slave_id"]
|
||||||
logfile.frames = data["frames"]
|
logfile.frames = data["frames"]
|
||||||
|
|
||||||
return logfile
|
return logfile
|
||||||
|
|
||||||
class RenderSlave:
|
class RenderSlave:
|
||||||
_slave_map = {}
|
_slave_map = {}
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.id = ""
|
|
||||||
self.name = ""
|
|
||||||
self.address = ("",0)
|
|
||||||
self.stats = ""
|
|
||||||
self.total_done = 0
|
|
||||||
self.total_error = 0
|
|
||||||
self.last_seen = 0.0
|
|
||||||
|
|
||||||
def serialize(self):
|
|
||||||
return {
|
|
||||||
"id": self.id,
|
|
||||||
"name": self.name,
|
|
||||||
"address": self.address,
|
|
||||||
"stats": self.stats,
|
|
||||||
"total_done": self.total_done,
|
|
||||||
"total_error": self.total_error,
|
|
||||||
"last_seen": self.last_seen
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def materialize(data, cache = True):
|
|
||||||
if not data:
|
|
||||||
return None
|
|
||||||
|
|
||||||
slave_id = data["id"]
|
|
||||||
|
|
||||||
if cache and slave_id in RenderSlave._slave_map:
|
def __init__(self):
|
||||||
return RenderSlave._slave_map[slave_id]
|
self.id = ""
|
||||||
|
self.name = ""
|
||||||
|
self.address = ("",0)
|
||||||
|
self.stats = ""
|
||||||
|
self.total_done = 0
|
||||||
|
self.total_error = 0
|
||||||
|
self.last_seen = 0.0
|
||||||
|
|
||||||
slave = RenderSlave()
|
def serialize(self):
|
||||||
slave.id = slave_id
|
return {
|
||||||
slave.name = data["name"]
|
"id": self.id,
|
||||||
slave.address = data["address"]
|
"name": self.name,
|
||||||
slave.stats = data["stats"]
|
"address": self.address,
|
||||||
slave.total_done = data["total_done"]
|
"stats": self.stats,
|
||||||
slave.total_error = data["total_error"]
|
"total_done": self.total_done,
|
||||||
slave.last_seen = data["last_seen"]
|
"total_error": self.total_error,
|
||||||
|
"last_seen": self.last_seen
|
||||||
|
}
|
||||||
|
|
||||||
if cache:
|
@staticmethod
|
||||||
RenderSlave._slave_map[slave_id] = slave
|
def materialize(data, cache = True):
|
||||||
|
if not data:
|
||||||
return slave
|
return None
|
||||||
|
|
||||||
|
slave_id = data["id"]
|
||||||
|
|
||||||
|
if cache and slave_id in RenderSlave._slave_map:
|
||||||
|
return RenderSlave._slave_map[slave_id]
|
||||||
|
|
||||||
|
slave = RenderSlave()
|
||||||
|
slave.id = slave_id
|
||||||
|
slave.name = data["name"]
|
||||||
|
slave.address = data["address"]
|
||||||
|
slave.stats = data["stats"]
|
||||||
|
slave.total_done = data["total_done"]
|
||||||
|
slave.total_error = data["total_error"]
|
||||||
|
slave.last_seen = data["last_seen"]
|
||||||
|
|
||||||
|
if cache:
|
||||||
|
RenderSlave._slave_map[slave_id] = slave
|
||||||
|
|
||||||
|
return slave
|
||||||
|
|
||||||
JOB_BLENDER = 1
|
JOB_BLENDER = 1
|
||||||
JOB_PROCESS = 2
|
JOB_PROCESS = 2
|
||||||
|
|
||||||
JOB_TYPES = {
|
JOB_TYPES = {
|
||||||
JOB_BLENDER: "Blender",
|
JOB_BLENDER: "Blender",
|
||||||
JOB_PROCESS: "Process"
|
JOB_PROCESS: "Process"
|
||||||
}
|
}
|
||||||
|
|
||||||
class RenderFile:
|
class RenderFile:
|
||||||
def __init__(self, filepath = "", index = 0, start = -1, end = -1):
|
def __init__(self, filepath = "", index = 0, start = -1, end = -1):
|
||||||
self.filepath = filepath
|
self.filepath = filepath
|
||||||
self.index = index
|
self.index = index
|
||||||
self.start = start
|
self.start = start
|
||||||
self.end = end
|
self.end = end
|
||||||
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
return {
|
return {
|
||||||
"filepath": self.filepath,
|
"filepath": self.filepath,
|
||||||
"index": self.index,
|
"index": self.index,
|
||||||
"start": self.start,
|
"start": self.start,
|
||||||
"end": self.end
|
"end": self.end
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def materialize(data):
|
def materialize(data):
|
||||||
if not data:
|
if not data:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
rfile = RenderFile(data["filepath"], data["index"], data["start"], data["end"])
|
|
||||||
|
|
||||||
return rfile
|
rfile = RenderFile(data["filepath"], data["index"], data["start"], data["end"])
|
||||||
|
|
||||||
|
return rfile
|
||||||
|
|
||||||
class RenderJob:
|
class RenderJob:
|
||||||
def __init__(self, job_info = None):
|
def __init__(self, job_info = None):
|
||||||
self.id = ""
|
self.id = ""
|
||||||
self.type = JOB_BLENDER
|
self.type = JOB_BLENDER
|
||||||
self.name = ""
|
self.name = ""
|
||||||
self.category = "None"
|
self.category = "None"
|
||||||
self.status = JOB_WAITING
|
self.status = JOB_WAITING
|
||||||
self.files = []
|
self.files = []
|
||||||
self.chunks = 0
|
self.chunks = 0
|
||||||
self.priority = 0
|
self.priority = 0
|
||||||
self.blacklist = []
|
self.blacklist = []
|
||||||
|
|
||||||
self.usage = 0.0
|
self.usage = 0.0
|
||||||
self.last_dispatched = 0.0
|
self.last_dispatched = 0.0
|
||||||
self.frames = []
|
self.frames = []
|
||||||
|
|
||||||
if job_info:
|
|
||||||
self.type = job_info.type
|
|
||||||
self.name = job_info.name
|
|
||||||
self.category = job_info.category
|
|
||||||
self.status = job_info.status
|
|
||||||
self.files = job_info.files
|
|
||||||
self.chunks = job_info.chunks
|
|
||||||
self.priority = job_info.priority
|
|
||||||
self.blacklist = job_info.blacklist
|
|
||||||
|
|
||||||
def addFile(self, file_path, start=-1, end=-1):
|
|
||||||
self.files.append(RenderFile(file_path, len(self.files), start, end))
|
|
||||||
|
|
||||||
def addFrame(self, frame_number, command = ""):
|
|
||||||
frame = RenderFrame(frame_number, command)
|
|
||||||
self.frames.append(frame)
|
|
||||||
return frame
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return len(self.frames)
|
|
||||||
|
|
||||||
def countFrames(self, status=QUEUED):
|
|
||||||
total = 0
|
|
||||||
for f in self.frames:
|
|
||||||
if f.status == status:
|
|
||||||
total += 1
|
|
||||||
|
|
||||||
return total
|
|
||||||
|
|
||||||
def countSlaves(self):
|
|
||||||
return len(set((frame.slave for frame in self.frames if frame.status == DISPATCHED)))
|
|
||||||
|
|
||||||
def statusText(self):
|
|
||||||
return JOB_STATUS_TEXT[self.status]
|
|
||||||
|
|
||||||
def framesStatus(self):
|
|
||||||
results = {
|
|
||||||
QUEUED: 0,
|
|
||||||
DISPATCHED: 0,
|
|
||||||
DONE: 0,
|
|
||||||
ERROR: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
for frame in self.frames:
|
|
||||||
results[frame.status] += 1
|
|
||||||
|
|
||||||
return results
|
|
||||||
|
|
||||||
def __contains__(self, frame_number):
|
|
||||||
for f in self.frames:
|
|
||||||
if f.number == frame_number:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def __getitem__(self, frame_number):
|
|
||||||
for f in self.frames:
|
|
||||||
if f.number == frame_number:
|
|
||||||
return f
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def serialize(self, frames = None):
|
|
||||||
min_frame = min((f.number for f in frames)) if frames else -1
|
|
||||||
max_frame = max((f.number for f in frames)) if frames else -1
|
|
||||||
return {
|
|
||||||
"id": self.id,
|
|
||||||
"type": self.type,
|
|
||||||
"name": self.name,
|
|
||||||
"category": self.category,
|
|
||||||
"status": self.status,
|
|
||||||
"files": [f.serialize() for f in self.files if f.start == -1 or not frames or (f.start <= max_frame and f.end >= min_frame)],
|
|
||||||
"frames": [f.serialize() for f in self.frames if not frames or f in frames],
|
|
||||||
"chunks": self.chunks,
|
|
||||||
"priority": self.priority,
|
|
||||||
"usage": self.usage,
|
|
||||||
"blacklist": self.blacklist,
|
|
||||||
"last_dispatched": self.last_dispatched
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
if job_info:
|
||||||
def materialize(data):
|
self.type = job_info.type
|
||||||
if not data:
|
self.name = job_info.name
|
||||||
return None
|
self.category = job_info.category
|
||||||
|
self.status = job_info.status
|
||||||
job = RenderJob()
|
self.files = job_info.files
|
||||||
job.id = data["id"]
|
self.chunks = job_info.chunks
|
||||||
job.type = data["type"]
|
self.priority = job_info.priority
|
||||||
job.name = data["name"]
|
self.blacklist = job_info.blacklist
|
||||||
job.category = data["category"]
|
|
||||||
job.status = data["status"]
|
|
||||||
job.files = [RenderFile.materialize(f) for f in data["files"]]
|
|
||||||
job.frames = [RenderFrame.materialize(f) for f in data["frames"]]
|
|
||||||
job.chunks = data["chunks"]
|
|
||||||
job.priority = data["priority"]
|
|
||||||
job.usage = data["usage"]
|
|
||||||
job.blacklist = data["blacklist"]
|
|
||||||
job.last_dispatched = data["last_dispatched"]
|
|
||||||
|
|
||||||
return job
|
def addFile(self, file_path, start=-1, end=-1):
|
||||||
|
self.files.append(RenderFile(file_path, len(self.files), start, end))
|
||||||
|
|
||||||
|
def addFrame(self, frame_number, command = ""):
|
||||||
|
frame = RenderFrame(frame_number, command)
|
||||||
|
self.frames.append(frame)
|
||||||
|
return frame
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.frames)
|
||||||
|
|
||||||
|
def countFrames(self, status=QUEUED):
|
||||||
|
total = 0
|
||||||
|
for f in self.frames:
|
||||||
|
if f.status == status:
|
||||||
|
total += 1
|
||||||
|
|
||||||
|
return total
|
||||||
|
|
||||||
|
def countSlaves(self):
|
||||||
|
return len(set((frame.slave for frame in self.frames if frame.status == DISPATCHED)))
|
||||||
|
|
||||||
|
def statusText(self):
|
||||||
|
return JOB_STATUS_TEXT[self.status]
|
||||||
|
|
||||||
|
def framesStatus(self):
|
||||||
|
results = {
|
||||||
|
QUEUED: 0,
|
||||||
|
DISPATCHED: 0,
|
||||||
|
DONE: 0,
|
||||||
|
ERROR: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
for frame in self.frames:
|
||||||
|
results[frame.status] += 1
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def __contains__(self, frame_number):
|
||||||
|
for f in self.frames:
|
||||||
|
if f.number == frame_number:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __getitem__(self, frame_number):
|
||||||
|
for f in self.frames:
|
||||||
|
if f.number == frame_number:
|
||||||
|
return f
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def serialize(self, frames = None):
|
||||||
|
min_frame = min((f.number for f in frames)) if frames else -1
|
||||||
|
max_frame = max((f.number for f in frames)) if frames else -1
|
||||||
|
return {
|
||||||
|
"id": self.id,
|
||||||
|
"type": self.type,
|
||||||
|
"name": self.name,
|
||||||
|
"category": self.category,
|
||||||
|
"status": self.status,
|
||||||
|
"files": [f.serialize() for f in self.files if f.start == -1 or not frames or (f.start <= max_frame and f.end >= min_frame)],
|
||||||
|
"frames": [f.serialize() for f in self.frames if not frames or f in frames],
|
||||||
|
"chunks": self.chunks,
|
||||||
|
"priority": self.priority,
|
||||||
|
"usage": self.usage,
|
||||||
|
"blacklist": self.blacklist,
|
||||||
|
"last_dispatched": self.last_dispatched
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def materialize(data):
|
||||||
|
if not data:
|
||||||
|
return None
|
||||||
|
|
||||||
|
job = RenderJob()
|
||||||
|
job.id = data["id"]
|
||||||
|
job.type = data["type"]
|
||||||
|
job.name = data["name"]
|
||||||
|
job.category = data["category"]
|
||||||
|
job.status = data["status"]
|
||||||
|
job.files = [RenderFile.materialize(f) for f in data["files"]]
|
||||||
|
job.frames = [RenderFrame.materialize(f) for f in data["frames"]]
|
||||||
|
job.chunks = data["chunks"]
|
||||||
|
job.priority = data["priority"]
|
||||||
|
job.usage = data["usage"]
|
||||||
|
job.blacklist = data["blacklist"]
|
||||||
|
job.last_dispatched = data["last_dispatched"]
|
||||||
|
|
||||||
|
return job
|
||||||
|
|
||||||
class RenderFrame:
|
class RenderFrame:
|
||||||
def __init__(self, number = 0, command = ""):
|
def __init__(self, number = 0, command = ""):
|
||||||
self.number = number
|
self.number = number
|
||||||
self.time = 0
|
self.time = 0
|
||||||
self.status = QUEUED
|
self.status = QUEUED
|
||||||
self.slave = None
|
self.slave = None
|
||||||
self.command = command
|
self.command = command
|
||||||
|
|
||||||
def statusText(self):
|
def statusText(self):
|
||||||
return FRAME_STATUS_TEXT[self.status]
|
return FRAME_STATUS_TEXT[self.status]
|
||||||
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
return {
|
return {
|
||||||
"number": self.number,
|
"number": self.number,
|
||||||
"time": self.time,
|
"time": self.time,
|
||||||
"status": self.status,
|
"status": self.status,
|
||||||
"slave": None if not self.slave else self.slave.serialize(),
|
"slave": None if not self.slave else self.slave.serialize(),
|
||||||
"command": self.command
|
"command": self.command
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def materialize(data):
|
|
||||||
if not data:
|
|
||||||
return None
|
|
||||||
|
|
||||||
frame = RenderFrame()
|
|
||||||
frame.number = data["number"]
|
|
||||||
frame.time = data["time"]
|
|
||||||
frame.status = data["status"]
|
|
||||||
frame.slave = RenderSlave.materialize(data["slave"])
|
|
||||||
frame.command = data["command"]
|
|
||||||
|
|
||||||
return frame
|
@staticmethod
|
||||||
|
def materialize(data):
|
||||||
|
if not data:
|
||||||
|
return None
|
||||||
|
|
||||||
|
frame = RenderFrame()
|
||||||
|
frame.number = data["number"]
|
||||||
|
frame.time = data["time"]
|
||||||
|
frame.status = data["status"]
|
||||||
|
frame.slave = RenderSlave.materialize(data["slave"])
|
||||||
|
frame.command = data["command"]
|
||||||
|
|
||||||
|
return frame
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
# as published by the Free Software Foundation; either version 2
|
# as published by the Free Software Foundation; either version 2
|
||||||
# of the License, or (at your option) any later version.
|
# of the License, or (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
@@ -28,414 +28,414 @@ import netrender.model
|
|||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class RENDER_OT_netslave_bake(bpy.types.Operator):
|
class RENDER_OT_netslave_bake(bpy.types.Operator):
|
||||||
'''NEED DESCRIPTION'''
|
'''NEED DESCRIPTION'''
|
||||||
bl_idname = "render.netslavebake"
|
bl_idname = "render.netslavebake"
|
||||||
bl_label = "Bake all in file"
|
bl_label = "Bake all in file"
|
||||||
|
|
||||||
def poll(self, context):
|
def poll(self, context):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
scene = context.scene
|
scene = context.scene
|
||||||
netsettings = scene.network_render
|
netsettings = scene.network_render
|
||||||
|
|
||||||
filename = bpy.data.filename
|
filename = bpy.data.filename
|
||||||
path, name = os.path.split(filename)
|
path, name = os.path.split(filename)
|
||||||
root, ext = os.path.splitext(name)
|
root, ext = os.path.splitext(name)
|
||||||
default_path = path + os.sep + "blendcache_" + root + os.sep # need an API call for that
|
default_path = path + os.sep + "blendcache_" + root + os.sep # need an API call for that
|
||||||
relative_path = os.sep + os.sep + "blendcache_" + root + os.sep
|
relative_path = os.sep + os.sep + "blendcache_" + root + os.sep
|
||||||
|
|
||||||
# Force all point cache next to the blend file
|
# Force all point cache next to the blend file
|
||||||
for object in bpy.data.objects:
|
for object in bpy.data.objects:
|
||||||
for modifier in object.modifiers:
|
for modifier in object.modifiers:
|
||||||
if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
|
if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
|
||||||
modifier.settings.path = relative_path
|
modifier.settings.path = relative_path
|
||||||
bpy.ops.fluid.bake({"active_object": object, "scene": scene})
|
bpy.ops.fluid.bake({"active_object": object, "scene": scene})
|
||||||
elif modifier.type == "CLOTH":
|
elif modifier.type == "CLOTH":
|
||||||
modifier.point_cache.step = 1
|
modifier.point_cache.step = 1
|
||||||
modifier.point_cache.disk_cache = True
|
modifier.point_cache.disk_cache = True
|
||||||
modifier.point_cache.external = False
|
modifier.point_cache.external = False
|
||||||
elif modifier.type == "SOFT_BODY":
|
elif modifier.type == "SOFT_BODY":
|
||||||
modifier.point_cache.step = 1
|
modifier.point_cache.step = 1
|
||||||
modifier.point_cache.disk_cache = True
|
modifier.point_cache.disk_cache = True
|
||||||
modifier.point_cache.external = False
|
modifier.point_cache.external = False
|
||||||
elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN":
|
elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN":
|
||||||
modifier.domain_settings.point_cache_low.step = 1
|
modifier.domain_settings.point_cache_low.step = 1
|
||||||
modifier.domain_settings.point_cache_low.disk_cache = True
|
modifier.domain_settings.point_cache_low.disk_cache = True
|
||||||
modifier.domain_settings.point_cache_low.external = False
|
modifier.domain_settings.point_cache_low.external = False
|
||||||
modifier.domain_settings.point_cache_high.step = 1
|
modifier.domain_settings.point_cache_high.step = 1
|
||||||
modifier.domain_settings.point_cache_high.disk_cache = True
|
modifier.domain_settings.point_cache_high.disk_cache = True
|
||||||
modifier.domain_settings.point_cache_high.external = False
|
modifier.domain_settings.point_cache_high.external = False
|
||||||
|
|
||||||
# particles modifier are stupid and don't contain data
|
# particles modifier are stupid and don't contain data
|
||||||
# we have to go through the object property
|
# we have to go through the object property
|
||||||
for psys in object.particle_systems:
|
for psys in object.particle_systems:
|
||||||
psys.point_cache.step = 1
|
psys.point_cache.step = 1
|
||||||
psys.point_cache.disk_cache = True
|
psys.point_cache.disk_cache = True
|
||||||
psys.point_cache.external = False
|
psys.point_cache.external = False
|
||||||
psys.point_cache.filepath = relative_path
|
psys.point_cache.filepath = relative_path
|
||||||
|
|
||||||
bpy.ops.ptcache.bake_all()
|
bpy.ops.ptcache.bake_all()
|
||||||
|
|
||||||
#bpy.ops.wm.save_mainfile(path = path + os.sep + root + "_baked.blend")
|
#bpy.ops.wm.save_mainfile(path = path + os.sep + root + "_baked.blend")
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
return self.execute(context)
|
return self.execute(context)
|
||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class RENDER_OT_netclientanim(bpy.types.Operator):
|
class RENDER_OT_netclientanim(bpy.types.Operator):
|
||||||
'''Start rendering an animation on network'''
|
'''Start rendering an animation on network'''
|
||||||
bl_idname = "render.netclientanim"
|
bl_idname = "render.netclientanim"
|
||||||
bl_label = "Animation on network"
|
bl_label = "Animation on network"
|
||||||
|
|
||||||
def poll(self, context):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
scene = context.scene
|
|
||||||
netsettings = scene.network_render
|
|
||||||
|
|
||||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
|
||||||
|
|
||||||
if conn:
|
def poll(self, context):
|
||||||
# Sending file
|
return True
|
||||||
scene.network_render.job_id = client.clientSendJob(conn, scene, True)
|
|
||||||
conn.close()
|
def execute(self, context):
|
||||||
|
scene = context.scene
|
||||||
bpy.ops.screen.render('INVOKE_AREA', animation=True)
|
netsettings = scene.network_render
|
||||||
|
|
||||||
return {'FINISHED'}
|
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||||
|
|
||||||
def invoke(self, context, event):
|
if conn:
|
||||||
return self.execute(context)
|
# Sending file
|
||||||
|
scene.network_render.job_id = client.clientSendJob(conn, scene, True)
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
bpy.ops.screen.render('INVOKE_AREA', animation=True)
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
return self.execute(context)
|
||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class RENDER_OT_netclientsend(bpy.types.Operator):
|
class RENDER_OT_netclientsend(bpy.types.Operator):
|
||||||
'''Send Render Job to the Network'''
|
'''Send Render Job to the Network'''
|
||||||
bl_idname = "render.netclientsend"
|
bl_idname = "render.netclientsend"
|
||||||
bl_label = "Send job"
|
bl_label = "Send job"
|
||||||
|
|
||||||
def poll(self, context):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
scene = context.scene
|
|
||||||
netsettings = scene.network_render
|
|
||||||
|
|
||||||
try:
|
|
||||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
|
||||||
|
|
||||||
if conn:
|
def poll(self, context):
|
||||||
# Sending file
|
return True
|
||||||
scene.network_render.job_id = client.clientSendJob(conn, scene, True)
|
|
||||||
conn.close()
|
def execute(self, context):
|
||||||
self.report('INFO', "Job sent to master")
|
scene = context.scene
|
||||||
except Exception as err:
|
netsettings = scene.network_render
|
||||||
self.report('ERROR', str(err))
|
|
||||||
|
try:
|
||||||
|
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||||
return {'FINISHED'}
|
|
||||||
|
if conn:
|
||||||
def invoke(self, context, event):
|
# Sending file
|
||||||
return self.execute(context)
|
scene.network_render.job_id = client.clientSendJob(conn, scene, True)
|
||||||
|
conn.close()
|
||||||
|
self.report('INFO', "Job sent to master")
|
||||||
|
except Exception as err:
|
||||||
|
self.report('ERROR', str(err))
|
||||||
|
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
return self.execute(context)
|
||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class RENDER_OT_netclientstatus(bpy.types.Operator):
|
class RENDER_OT_netclientstatus(bpy.types.Operator):
|
||||||
'''Refresh the status of the current jobs'''
|
'''Refresh the status of the current jobs'''
|
||||||
bl_idname = "render.netclientstatus"
|
bl_idname = "render.netclientstatus"
|
||||||
bl_label = "Client Status"
|
bl_label = "Client Status"
|
||||||
|
|
||||||
def poll(self, context):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
netsettings = context.scene.network_render
|
|
||||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
|
||||||
|
|
||||||
if conn:
|
def poll(self, context):
|
||||||
conn.request("GET", "/status")
|
return True
|
||||||
|
|
||||||
response = conn.getresponse()
|
def execute(self, context):
|
||||||
print( response.status, response.reason )
|
netsettings = context.scene.network_render
|
||||||
|
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||||
jobs = (netrender.model.RenderJob.materialize(j) for j in eval(str(response.read(), encoding='utf8')))
|
|
||||||
|
if conn:
|
||||||
while(len(netsettings.jobs) > 0):
|
conn.request("GET", "/status")
|
||||||
netsettings.jobs.remove(0)
|
|
||||||
|
response = conn.getresponse()
|
||||||
netrender.jobs = []
|
print( response.status, response.reason )
|
||||||
|
|
||||||
for j in jobs:
|
jobs = (netrender.model.RenderJob.materialize(j) for j in eval(str(response.read(), encoding='utf8')))
|
||||||
netrender.jobs.append(j)
|
|
||||||
netsettings.jobs.add()
|
while(len(netsettings.jobs) > 0):
|
||||||
job = netsettings.jobs[-1]
|
netsettings.jobs.remove(0)
|
||||||
|
|
||||||
j.results = j.framesStatus() # cache frame status
|
netrender.jobs = []
|
||||||
|
|
||||||
job.name = j.name
|
for j in jobs:
|
||||||
|
netrender.jobs.append(j)
|
||||||
return {'FINISHED'}
|
netsettings.jobs.add()
|
||||||
|
job = netsettings.jobs[-1]
|
||||||
def invoke(self, context, event):
|
|
||||||
return self.execute(context)
|
j.results = j.framesStatus() # cache frame status
|
||||||
|
|
||||||
|
job.name = j.name
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
return self.execute(context)
|
||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class RENDER_OT_netclientblacklistslave(bpy.types.Operator):
|
class RENDER_OT_netclientblacklistslave(bpy.types.Operator):
|
||||||
'''Operator documentation text, will be used for the operator tooltip and python docs.'''
|
'''Operator documentation text, will be used for the operator tooltip and python docs.'''
|
||||||
bl_idname = "render.netclientblacklistslave"
|
bl_idname = "render.netclientblacklistslave"
|
||||||
bl_label = "Client Blacklist Slave"
|
bl_label = "Client Blacklist Slave"
|
||||||
|
|
||||||
def poll(self, context):
|
def poll(self, context):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
netsettings = context.scene.network_render
|
netsettings = context.scene.network_render
|
||||||
|
|
||||||
if netsettings.active_slave_index >= 0:
|
if netsettings.active_slave_index >= 0:
|
||||||
|
|
||||||
# deal with data
|
# deal with data
|
||||||
slave = netrender.slaves.pop(netsettings.active_slave_index)
|
slave = netrender.slaves.pop(netsettings.active_slave_index)
|
||||||
netrender.blacklist.append(slave)
|
netrender.blacklist.append(slave)
|
||||||
|
|
||||||
# deal with rna
|
# deal with rna
|
||||||
netsettings.slaves_blacklist.add()
|
netsettings.slaves_blacklist.add()
|
||||||
netsettings.slaves_blacklist[-1].name = slave.name
|
netsettings.slaves_blacklist[-1].name = slave.name
|
||||||
|
|
||||||
netsettings.slaves.remove(netsettings.active_slave_index)
|
netsettings.slaves.remove(netsettings.active_slave_index)
|
||||||
netsettings.active_slave_index = -1
|
netsettings.active_slave_index = -1
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
return self.execute(context)
|
return self.execute(context)
|
||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class RENDER_OT_netclientwhitelistslave(bpy.types.Operator):
|
class RENDER_OT_netclientwhitelistslave(bpy.types.Operator):
|
||||||
'''Operator documentation text, will be used for the operator tooltip and python docs.'''
|
'''Operator documentation text, will be used for the operator tooltip and python docs.'''
|
||||||
bl_idname = "render.netclientwhitelistslave"
|
bl_idname = "render.netclientwhitelistslave"
|
||||||
bl_label = "Client Whitelist Slave"
|
bl_label = "Client Whitelist Slave"
|
||||||
|
|
||||||
def poll(self, context):
|
def poll(self, context):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
netsettings = context.scene.network_render
|
netsettings = context.scene.network_render
|
||||||
|
|
||||||
if netsettings.active_blacklisted_slave_index >= 0:
|
if netsettings.active_blacklisted_slave_index >= 0:
|
||||||
|
|
||||||
# deal with data
|
# deal with data
|
||||||
slave = netrender.blacklist.pop(netsettings.active_blacklisted_slave_index)
|
slave = netrender.blacklist.pop(netsettings.active_blacklisted_slave_index)
|
||||||
netrender.slaves.append(slave)
|
netrender.slaves.append(slave)
|
||||||
|
|
||||||
# deal with rna
|
# deal with rna
|
||||||
netsettings.slaves.add()
|
netsettings.slaves.add()
|
||||||
netsettings.slaves[-1].name = slave.name
|
netsettings.slaves[-1].name = slave.name
|
||||||
|
|
||||||
netsettings.slaves_blacklist.remove(netsettings.active_blacklisted_slave_index)
|
netsettings.slaves_blacklist.remove(netsettings.active_blacklisted_slave_index)
|
||||||
netsettings.active_blacklisted_slave_index = -1
|
netsettings.active_blacklisted_slave_index = -1
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
return self.execute(context)
|
return self.execute(context)
|
||||||
|
|
||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class RENDER_OT_netclientslaves(bpy.types.Operator):
|
class RENDER_OT_netclientslaves(bpy.types.Operator):
|
||||||
'''Refresh status about available Render slaves'''
|
'''Refresh status about available Render slaves'''
|
||||||
bl_idname = "render.netclientslaves"
|
bl_idname = "render.netclientslaves"
|
||||||
bl_label = "Client Slaves"
|
bl_label = "Client Slaves"
|
||||||
|
|
||||||
def poll(self, context):
|
def poll(self, context):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
netsettings = context.scene.network_render
|
netsettings = context.scene.network_render
|
||||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||||
|
|
||||||
if conn:
|
if conn:
|
||||||
conn.request("GET", "/slaves")
|
conn.request("GET", "/slaves")
|
||||||
|
|
||||||
response = conn.getresponse()
|
response = conn.getresponse()
|
||||||
print( response.status, response.reason )
|
print( response.status, response.reason )
|
||||||
|
|
||||||
slaves = (netrender.model.RenderSlave.materialize(s) for s in eval(str(response.read(), encoding='utf8')))
|
slaves = (netrender.model.RenderSlave.materialize(s) for s in eval(str(response.read(), encoding='utf8')))
|
||||||
|
|
||||||
while(len(netsettings.slaves) > 0):
|
while(len(netsettings.slaves) > 0):
|
||||||
netsettings.slaves.remove(0)
|
netsettings.slaves.remove(0)
|
||||||
|
|
||||||
netrender.slaves = []
|
netrender.slaves = []
|
||||||
|
|
||||||
for s in slaves:
|
for s in slaves:
|
||||||
for i in range(len(netrender.blacklist)):
|
for i in range(len(netrender.blacklist)):
|
||||||
slave = netrender.blacklist[i]
|
slave = netrender.blacklist[i]
|
||||||
if slave.id == s.id:
|
if slave.id == s.id:
|
||||||
netrender.blacklist[i] = s
|
netrender.blacklist[i] = s
|
||||||
netsettings.slaves_blacklist[i].name = s.name
|
netsettings.slaves_blacklist[i].name = s.name
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
netrender.slaves.append(s)
|
netrender.slaves.append(s)
|
||||||
|
|
||||||
netsettings.slaves.add()
|
netsettings.slaves.add()
|
||||||
slave = netsettings.slaves[-1]
|
slave = netsettings.slaves[-1]
|
||||||
slave.name = s.name
|
slave.name = s.name
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
return self.execute(context)
|
return self.execute(context)
|
||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class RENDER_OT_netclientcancel(bpy.types.Operator):
|
class RENDER_OT_netclientcancel(bpy.types.Operator):
|
||||||
'''Cancel the selected network rendering job.'''
|
'''Cancel the selected network rendering job.'''
|
||||||
bl_idname = "render.netclientcancel"
|
bl_idname = "render.netclientcancel"
|
||||||
bl_label = "Client Cancel"
|
bl_label = "Client Cancel"
|
||||||
|
|
||||||
def poll(self, context):
|
def poll(self, context):
|
||||||
netsettings = context.scene.network_render
|
netsettings = context.scene.network_render
|
||||||
return netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0
|
return netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
netsettings = context.scene.network_render
|
netsettings = context.scene.network_render
|
||||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||||
|
|
||||||
if conn:
|
if conn:
|
||||||
job = netrender.jobs[netsettings.active_job_index]
|
job = netrender.jobs[netsettings.active_job_index]
|
||||||
|
|
||||||
conn.request("POST", cancelURL(job.id))
|
conn.request("POST", cancelURL(job.id))
|
||||||
|
|
||||||
response = conn.getresponse()
|
response = conn.getresponse()
|
||||||
print( response.status, response.reason )
|
print( response.status, response.reason )
|
||||||
|
|
||||||
|
netsettings.jobs.remove(netsettings.active_job_index)
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
return self.execute(context)
|
||||||
|
|
||||||
netsettings.jobs.remove(netsettings.active_job_index)
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
def invoke(self, context, event):
|
|
||||||
return self.execute(context)
|
|
||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class RENDER_OT_netclientcancelall(bpy.types.Operator):
|
class RENDER_OT_netclientcancelall(bpy.types.Operator):
|
||||||
'''Cancel all running network rendering jobs.'''
|
'''Cancel all running network rendering jobs.'''
|
||||||
bl_idname = "render.netclientcancelall"
|
bl_idname = "render.netclientcancelall"
|
||||||
bl_label = "Client Cancel All"
|
bl_label = "Client Cancel All"
|
||||||
|
|
||||||
def poll(self, context):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
netsettings = context.scene.network_render
|
|
||||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
|
||||||
|
|
||||||
if conn:
|
|
||||||
conn.request("POST", "/clear")
|
|
||||||
|
|
||||||
response = conn.getresponse()
|
|
||||||
print( response.status, response.reason )
|
|
||||||
|
|
||||||
while(len(netsettings.jobs) > 0):
|
|
||||||
netsettings.jobs.remove(0)
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
def poll(self, context):
|
||||||
|
return True
|
||||||
def invoke(self, context, event):
|
|
||||||
return self.execute(context)
|
def execute(self, context):
|
||||||
|
netsettings = context.scene.network_render
|
||||||
|
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||||
|
|
||||||
|
if conn:
|
||||||
|
conn.request("POST", "/clear")
|
||||||
|
|
||||||
|
response = conn.getresponse()
|
||||||
|
print( response.status, response.reason )
|
||||||
|
|
||||||
|
while(len(netsettings.jobs) > 0):
|
||||||
|
netsettings.jobs.remove(0)
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
return self.execute(context)
|
||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class netclientdownload(bpy.types.Operator):
|
class netclientdownload(bpy.types.Operator):
|
||||||
'''Download render results from the network'''
|
'''Download render results from the network'''
|
||||||
bl_idname = "render.netclientdownload"
|
bl_idname = "render.netclientdownload"
|
||||||
bl_label = "Client Download"
|
bl_label = "Client Download"
|
||||||
|
|
||||||
def poll(self, context):
|
def poll(self, context):
|
||||||
netsettings = context.scene.network_render
|
netsettings = context.scene.network_render
|
||||||
return netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0
|
return netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
netsettings = context.scene.network_render
|
netsettings = context.scene.network_render
|
||||||
rd = context.scene.render_data
|
rd = context.scene.render_data
|
||||||
|
|
||||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||||
|
|
||||||
if conn:
|
if conn:
|
||||||
job = netrender.jobs[netsettings.active_job_index]
|
job = netrender.jobs[netsettings.active_job_index]
|
||||||
|
|
||||||
for frame in job.frames:
|
for frame in job.frames:
|
||||||
client.requestResult(conn, job.id, frame.number)
|
client.requestResult(conn, job.id, frame.number)
|
||||||
response = conn.getresponse()
|
response = conn.getresponse()
|
||||||
|
|
||||||
if response.status != http.client.OK:
|
if response.status != http.client.OK:
|
||||||
print("missing", frame.number)
|
print("missing", frame.number)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print("got back", frame.number)
|
print("got back", frame.number)
|
||||||
|
|
||||||
f = open(netsettings.path + "%06d" % frame.number + ".exr", "wb")
|
f = open(netsettings.path + "%06d" % frame.number + ".exr", "wb")
|
||||||
buf = response.read(1024)
|
buf = response.read(1024)
|
||||||
|
|
||||||
while buf:
|
while buf:
|
||||||
f.write(buf)
|
f.write(buf)
|
||||||
buf = response.read(1024)
|
buf = response.read(1024)
|
||||||
|
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
return self.execute(context)
|
return self.execute(context)
|
||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class netclientscan(bpy.types.Operator):
|
class netclientscan(bpy.types.Operator):
|
||||||
__slots__ = []
|
__slots__ = []
|
||||||
'''Operator documentation text, will be used for the operator tooltip and python docs.'''
|
'''Operator documentation text, will be used for the operator tooltip and python docs.'''
|
||||||
bl_idname = "render.netclientscan"
|
bl_idname = "render.netclientscan"
|
||||||
bl_label = "Client Scan"
|
bl_label = "Client Scan"
|
||||||
|
|
||||||
def poll(self, context):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
address, port = clientScan(self.report)
|
|
||||||
|
|
||||||
if address:
|
def poll(self, context):
|
||||||
scene = context.scene
|
return True
|
||||||
netsettings = scene.network_render
|
|
||||||
netsettings.server_address = address
|
|
||||||
netsettings.server_port = port
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def execute(self, context):
|
||||||
print(dir(self))
|
address, port = clientScan(self.report)
|
||||||
return self.execute(context)
|
|
||||||
|
if address:
|
||||||
|
scene = context.scene
|
||||||
|
netsettings = scene.network_render
|
||||||
|
netsettings.server_address = address
|
||||||
|
netsettings.server_port = port
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
print(dir(self))
|
||||||
|
return self.execute(context)
|
||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class netclientweb(bpy.types.Operator):
|
class netclientweb(bpy.types.Operator):
|
||||||
'''Open new window with information about running rendering jobs'''
|
'''Open new window with information about running rendering jobs'''
|
||||||
bl_idname = "render.netclientweb"
|
bl_idname = "render.netclientweb"
|
||||||
bl_label = "Open Master Monitor"
|
bl_label = "Open Master Monitor"
|
||||||
|
|
||||||
def poll(self, context):
|
def poll(self, context):
|
||||||
netsettings = context.scene.network_render
|
netsettings = context.scene.network_render
|
||||||
return netsettings.server_address != "[default]"
|
return netsettings.server_address != "[default]"
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
netsettings = context.scene.network_render
|
netsettings = context.scene.network_render
|
||||||
|
|
||||||
|
|
||||||
# open connection to make sure server exists
|
# open connection to make sure server exists
|
||||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||||
|
|
||||||
if conn:
|
if conn:
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
webbrowser.open("http://%s:%i" % (netsettings.server_address, netsettings.server_port))
|
webbrowser.open("http://%s:%i" % (netsettings.server_address, netsettings.server_port))
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
return self.execute(context)
|
return self.execute(context)
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
# as published by the Free Software Foundation; either version 2
|
# as published by the Free Software Foundation; either version 2
|
||||||
# of the License, or (at your option) any later version.
|
# of the License, or (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
@@ -30,218 +30,218 @@ MAX_TIMEOUT = 10
|
|||||||
INCREMENT_TIMEOUT = 1
|
INCREMENT_TIMEOUT = 1
|
||||||
|
|
||||||
if platform.system() == 'Windows' and platform.version() >= '5': # Error mode is only available on Win2k or higher, that's version 5
|
if platform.system() == 'Windows' and platform.version() >= '5': # Error mode is only available on Win2k or higher, that's version 5
|
||||||
import ctypes
|
import ctypes
|
||||||
def SetErrorMode():
|
def SetErrorMode():
|
||||||
val = ctypes.windll.kernel32.SetErrorMode(0x0002)
|
val = ctypes.windll.kernel32.SetErrorMode(0x0002)
|
||||||
ctypes.windll.kernel32.SetErrorMode(val | 0x0002)
|
ctypes.windll.kernel32.SetErrorMode(val | 0x0002)
|
||||||
return val
|
return val
|
||||||
|
|
||||||
def RestoreErrorMode(val):
|
def RestoreErrorMode(val):
|
||||||
ctypes.windll.kernel32.SetErrorMode(val)
|
ctypes.windll.kernel32.SetErrorMode(val)
|
||||||
else:
|
else:
|
||||||
def SetErrorMode():
|
def SetErrorMode():
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def RestoreErrorMode(val):
|
def RestoreErrorMode(val):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def slave_Info():
|
def slave_Info():
|
||||||
sysname, nodename, release, version, machine, processor = platform.uname()
|
sysname, nodename, release, version, machine, processor = platform.uname()
|
||||||
slave = netrender.model.RenderSlave()
|
slave = netrender.model.RenderSlave()
|
||||||
slave.name = nodename
|
slave.name = nodename
|
||||||
slave.stats = sysname + " " + release + " " + machine + " " + processor
|
slave.stats = sysname + " " + release + " " + machine + " " + processor
|
||||||
return slave
|
return slave
|
||||||
|
|
||||||
def testCancel(conn, job_id, frame_number):
|
def testCancel(conn, job_id, frame_number):
|
||||||
conn.request("HEAD", "/status", headers={"job-id":job_id, "job-frame": str(frame_number)})
|
conn.request("HEAD", "/status", headers={"job-id":job_id, "job-frame": str(frame_number)})
|
||||||
|
|
||||||
# cancelled if job isn't found anymore
|
# cancelled if job isn't found anymore
|
||||||
if conn.getresponse().status == http.client.NO_CONTENT:
|
if conn.getresponse().status == http.client.NO_CONTENT:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def testFile(conn, job_id, slave_id, file_index, JOB_PREFIX, file_path, main_path = None):
|
def testFile(conn, job_id, slave_id, file_index, JOB_PREFIX, file_path, main_path = None):
|
||||||
job_full_path = prefixPath(JOB_PREFIX, file_path, main_path)
|
job_full_path = prefixPath(JOB_PREFIX, file_path, main_path)
|
||||||
|
|
||||||
if not os.path.exists(job_full_path):
|
if not os.path.exists(job_full_path):
|
||||||
temp_path = JOB_PREFIX + "slave.temp.blend"
|
temp_path = JOB_PREFIX + "slave.temp.blend"
|
||||||
conn.request("GET", fileURL(job_id, file_index), headers={"slave-id":slave_id})
|
conn.request("GET", fileURL(job_id, file_index), headers={"slave-id":slave_id})
|
||||||
response = conn.getresponse()
|
response = conn.getresponse()
|
||||||
|
|
||||||
if response.status != http.client.OK:
|
if response.status != http.client.OK:
|
||||||
return None # file for job not returned by server, need to return an error code to server
|
return None # file for job not returned by server, need to return an error code to server
|
||||||
|
|
||||||
f = open(temp_path, "wb")
|
f = open(temp_path, "wb")
|
||||||
buf = response.read(1024)
|
buf = response.read(1024)
|
||||||
|
|
||||||
while buf:
|
while buf:
|
||||||
f.write(buf)
|
f.write(buf)
|
||||||
buf = response.read(1024)
|
buf = response.read(1024)
|
||||||
|
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
os.renames(temp_path, job_full_path)
|
os.renames(temp_path, job_full_path)
|
||||||
|
|
||||||
return job_full_path
|
return job_full_path
|
||||||
|
|
||||||
def render_slave(engine, netsettings):
|
def render_slave(engine, netsettings):
|
||||||
timeout = 1
|
timeout = 1
|
||||||
|
|
||||||
engine.update_stats("", "Network render node initiation")
|
|
||||||
|
|
||||||
conn = clientConnection(netsettings.server_address, netsettings.server_port)
|
|
||||||
|
|
||||||
if conn:
|
|
||||||
conn.request("POST", "/slave", repr(slave_Info().serialize()))
|
|
||||||
response = conn.getresponse()
|
|
||||||
|
|
||||||
slave_id = response.getheader("slave-id")
|
|
||||||
|
|
||||||
NODE_PREFIX = netsettings.path + "slave_" + slave_id + os.sep
|
|
||||||
if not os.path.exists(NODE_PREFIX):
|
|
||||||
os.mkdir(NODE_PREFIX)
|
|
||||||
|
|
||||||
while not engine.test_break():
|
|
||||||
|
|
||||||
conn.request("GET", "/job", headers={"slave-id":slave_id})
|
|
||||||
response = conn.getresponse()
|
|
||||||
|
|
||||||
if response.status == http.client.OK:
|
|
||||||
timeout = 1 # reset timeout on new job
|
|
||||||
|
|
||||||
job = netrender.model.RenderJob.materialize(eval(str(response.read(), encoding='utf8')))
|
|
||||||
|
|
||||||
JOB_PREFIX = NODE_PREFIX + "job_" + job.id + os.sep
|
|
||||||
if not os.path.exists(JOB_PREFIX):
|
|
||||||
os.mkdir(JOB_PREFIX)
|
|
||||||
|
|
||||||
|
|
||||||
if job.type == netrender.model.JOB_BLENDER:
|
|
||||||
job_path = job.files[0].filepath # path of main file
|
|
||||||
main_path, main_file = os.path.split(job_path)
|
|
||||||
|
|
||||||
job_full_path = testFile(conn, job.id, slave_id, 0, JOB_PREFIX, job_path)
|
|
||||||
print("Fullpath", job_full_path)
|
|
||||||
print("File:", main_file, "and %i other files" % (len(job.files) - 1,))
|
|
||||||
engine.update_stats("", "Render File "+ main_file+ " for job "+ job.id)
|
|
||||||
|
|
||||||
for rfile in job.files[1:]:
|
|
||||||
print("\t", rfile.filepath)
|
|
||||||
testFile(conn, job.id, slave_id, rfile.index, JOB_PREFIX, rfile.filepath, main_path)
|
|
||||||
|
|
||||||
# announce log to master
|
engine.update_stats("", "Network render node initiation")
|
||||||
logfile = netrender.model.LogFile(job.id, slave_id, [frame.number for frame in job.frames])
|
|
||||||
conn.request("POST", "/log", bytes(repr(logfile.serialize()), encoding='utf8'))
|
conn = clientConnection(netsettings.server_address, netsettings.server_port)
|
||||||
response = conn.getresponse()
|
|
||||||
|
if conn:
|
||||||
|
conn.request("POST", "/slave", repr(slave_Info().serialize()))
|
||||||
first_frame = job.frames[0].number
|
response = conn.getresponse()
|
||||||
|
|
||||||
# start render
|
slave_id = response.getheader("slave-id")
|
||||||
start_t = time.time()
|
|
||||||
|
NODE_PREFIX = netsettings.path + "slave_" + slave_id + os.sep
|
||||||
if job.type == netrender.model.JOB_BLENDER:
|
if not os.path.exists(NODE_PREFIX):
|
||||||
frame_args = []
|
os.mkdir(NODE_PREFIX)
|
||||||
|
|
||||||
for frame in job.frames:
|
while not engine.test_break():
|
||||||
print("frame", frame.number)
|
|
||||||
frame_args += ["-f", str(frame.number)]
|
conn.request("GET", "/job", headers={"slave-id":slave_id})
|
||||||
|
response = conn.getresponse()
|
||||||
val = SetErrorMode()
|
|
||||||
process = subprocess.Popen([BLENDER_PATH, "-b", "-noaudio", job_full_path, "-o", JOB_PREFIX + "######", "-E", "BLENDER_RENDER", "-F", "MULTILAYER"] + frame_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
if response.status == http.client.OK:
|
||||||
RestoreErrorMode(val)
|
timeout = 1 # reset timeout on new job
|
||||||
elif job.type == netrender.model.JOB_PROCESS:
|
|
||||||
command = job.frames[0].command
|
job = netrender.model.RenderJob.materialize(eval(str(response.read(), encoding='utf8')))
|
||||||
val = SetErrorMode()
|
|
||||||
process = subprocess.Popen(command.split(" "), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
JOB_PREFIX = NODE_PREFIX + "job_" + job.id + os.sep
|
||||||
RestoreErrorMode(val)
|
if not os.path.exists(JOB_PREFIX):
|
||||||
|
os.mkdir(JOB_PREFIX)
|
||||||
headers = {"slave-id":slave_id}
|
|
||||||
|
|
||||||
cancelled = False
|
if job.type == netrender.model.JOB_BLENDER:
|
||||||
stdout = bytes()
|
job_path = job.files[0].filepath # path of main file
|
||||||
run_t = time.time()
|
main_path, main_file = os.path.split(job_path)
|
||||||
while process.poll() == None and not cancelled:
|
|
||||||
stdout += process.stdout.read(32)
|
job_full_path = testFile(conn, job.id, slave_id, 0, JOB_PREFIX, job_path)
|
||||||
current_t = time.time()
|
print("Fullpath", job_full_path)
|
||||||
cancelled = engine.test_break()
|
print("File:", main_file, "and %i other files" % (len(job.files) - 1,))
|
||||||
if current_t - run_t > CANCEL_POLL_SPEED:
|
engine.update_stats("", "Render File "+ main_file+ " for job "+ job.id)
|
||||||
|
|
||||||
# update logs if needed
|
for rfile in job.files[1:]:
|
||||||
if stdout:
|
print("\t", rfile.filepath)
|
||||||
# (only need to update on one frame, they are linked
|
testFile(conn, job.id, slave_id, rfile.index, JOB_PREFIX, rfile.filepath, main_path)
|
||||||
conn.request("PUT", logURL(job.id, first_frame), stdout, headers=headers)
|
|
||||||
response = conn.getresponse()
|
# announce log to master
|
||||||
|
logfile = netrender.model.LogFile(job.id, slave_id, [frame.number for frame in job.frames])
|
||||||
stdout = bytes()
|
conn.request("POST", "/log", bytes(repr(logfile.serialize()), encoding='utf8'))
|
||||||
|
response = conn.getresponse()
|
||||||
run_t = current_t
|
|
||||||
if testCancel(conn, job.id, first_frame):
|
|
||||||
cancelled = True
|
first_frame = job.frames[0].number
|
||||||
|
|
||||||
# read leftovers if needed
|
# start render
|
||||||
stdout += process.stdout.read()
|
start_t = time.time()
|
||||||
|
|
||||||
if cancelled:
|
if job.type == netrender.model.JOB_BLENDER:
|
||||||
# kill process if needed
|
frame_args = []
|
||||||
if process.poll() == None:
|
|
||||||
process.terminate()
|
for frame in job.frames:
|
||||||
continue # to next frame
|
print("frame", frame.number)
|
||||||
|
frame_args += ["-f", str(frame.number)]
|
||||||
total_t = time.time() - start_t
|
|
||||||
|
val = SetErrorMode()
|
||||||
avg_t = total_t / len(job.frames)
|
process = subprocess.Popen([BLENDER_PATH, "-b", "-noaudio", job_full_path, "-o", JOB_PREFIX + "######", "-E", "BLENDER_RENDER", "-F", "MULTILAYER"] + frame_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
RestoreErrorMode(val)
|
||||||
status = process.returncode
|
elif job.type == netrender.model.JOB_PROCESS:
|
||||||
|
command = job.frames[0].command
|
||||||
print("status", status)
|
val = SetErrorMode()
|
||||||
|
process = subprocess.Popen(command.split(" "), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
# flush the rest of the logs
|
RestoreErrorMode(val)
|
||||||
if stdout:
|
|
||||||
# (only need to update on one frame, they are linked
|
headers = {"slave-id":slave_id}
|
||||||
conn.request("PUT", logURL(job.id, first_frame), stdout, headers=headers)
|
|
||||||
if conn.getresponse().status == http.client.NO_CONTENT:
|
cancelled = False
|
||||||
continue
|
stdout = bytes()
|
||||||
|
run_t = time.time()
|
||||||
headers = {"job-id":job.id, "slave-id":slave_id, "job-time":str(avg_t)}
|
while process.poll() == None and not cancelled:
|
||||||
|
stdout += process.stdout.read(32)
|
||||||
|
current_t = time.time()
|
||||||
if status == 0: # non zero status is error
|
cancelled = engine.test_break()
|
||||||
headers["job-result"] = str(DONE)
|
if current_t - run_t > CANCEL_POLL_SPEED:
|
||||||
for frame in job.frames:
|
|
||||||
headers["job-frame"] = str(frame.number)
|
# update logs if needed
|
||||||
|
if stdout:
|
||||||
if job.type == netrender.model.JOB_BLENDER:
|
# (only need to update on one frame, they are linked
|
||||||
# send image back to server
|
conn.request("PUT", logURL(job.id, first_frame), stdout, headers=headers)
|
||||||
f = open(JOB_PREFIX + "%06d" % frame.number + ".exr", 'rb')
|
response = conn.getresponse()
|
||||||
conn.request("PUT", "/render", f, headers=headers)
|
|
||||||
f.close()
|
stdout = bytes()
|
||||||
if conn.getresponse().status == http.client.NO_CONTENT:
|
|
||||||
continue
|
run_t = current_t
|
||||||
elif job.type == netrender.model.JOB_PROCESS:
|
if testCancel(conn, job.id, first_frame):
|
||||||
conn.request("PUT", "/render", headers=headers)
|
cancelled = True
|
||||||
if conn.getresponse().status == http.client.NO_CONTENT:
|
|
||||||
continue
|
# read leftovers if needed
|
||||||
else:
|
stdout += process.stdout.read()
|
||||||
headers["job-result"] = str(ERROR)
|
|
||||||
for frame in job.frames:
|
if cancelled:
|
||||||
headers["job-frame"] = str(frame.number)
|
# kill process if needed
|
||||||
# send error result back to server
|
if process.poll() == None:
|
||||||
conn.request("PUT", "/render", headers=headers)
|
process.terminate()
|
||||||
if conn.getresponse().status == http.client.NO_CONTENT:
|
continue # to next frame
|
||||||
continue
|
|
||||||
else:
|
total_t = time.time() - start_t
|
||||||
if timeout < MAX_TIMEOUT:
|
|
||||||
timeout += INCREMENT_TIMEOUT
|
avg_t = total_t / len(job.frames)
|
||||||
|
|
||||||
for i in range(timeout):
|
status = process.returncode
|
||||||
time.sleep(1)
|
|
||||||
if engine.test_break():
|
print("status", status)
|
||||||
conn.close()
|
|
||||||
return
|
# flush the rest of the logs
|
||||||
|
if stdout:
|
||||||
conn.close()
|
# (only need to update on one frame, they are linked
|
||||||
|
conn.request("PUT", logURL(job.id, first_frame), stdout, headers=headers)
|
||||||
|
if conn.getresponse().status == http.client.NO_CONTENT:
|
||||||
|
continue
|
||||||
|
|
||||||
|
headers = {"job-id":job.id, "slave-id":slave_id, "job-time":str(avg_t)}
|
||||||
|
|
||||||
|
|
||||||
|
if status == 0: # non zero status is error
|
||||||
|
headers["job-result"] = str(DONE)
|
||||||
|
for frame in job.frames:
|
||||||
|
headers["job-frame"] = str(frame.number)
|
||||||
|
|
||||||
|
if job.type == netrender.model.JOB_BLENDER:
|
||||||
|
# send image back to server
|
||||||
|
f = open(JOB_PREFIX + "%06d" % frame.number + ".exr", 'rb')
|
||||||
|
conn.request("PUT", "/render", f, headers=headers)
|
||||||
|
f.close()
|
||||||
|
if conn.getresponse().status == http.client.NO_CONTENT:
|
||||||
|
continue
|
||||||
|
elif job.type == netrender.model.JOB_PROCESS:
|
||||||
|
conn.request("PUT", "/render", headers=headers)
|
||||||
|
if conn.getresponse().status == http.client.NO_CONTENT:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
headers["job-result"] = str(ERROR)
|
||||||
|
for frame in job.frames:
|
||||||
|
headers["job-frame"] = str(frame.number)
|
||||||
|
# send error result back to server
|
||||||
|
conn.request("PUT", "/render", headers=headers)
|
||||||
|
if conn.getresponse().status == http.client.NO_CONTENT:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
if timeout < MAX_TIMEOUT:
|
||||||
|
timeout += INCREMENT_TIMEOUT
|
||||||
|
|
||||||
|
for i in range(timeout):
|
||||||
|
time.sleep(1)
|
||||||
|
if engine.test_break():
|
||||||
|
conn.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
# as published by the Free Software Foundation; either version 2
|
# as published by the Free Software Foundation; either version 2
|
||||||
# of the License, or (at your option) any later version.
|
# of the License, or (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
@@ -37,319 +37,319 @@ DONE = 2
|
|||||||
ERROR = 3
|
ERROR = 3
|
||||||
|
|
||||||
class RenderButtonsPanel(bpy.types.Panel):
|
class RenderButtonsPanel(bpy.types.Panel):
|
||||||
bl_space_type = "PROPERTIES"
|
bl_space_type = "PROPERTIES"
|
||||||
bl_region_type = "WINDOW"
|
bl_region_type = "WINDOW"
|
||||||
bl_context = "render"
|
bl_context = "render"
|
||||||
# COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
|
# COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
|
||||||
|
|
||||||
def poll(self, context):
|
def poll(self, context):
|
||||||
rd = context.scene.render_data
|
rd = context.scene.render_data
|
||||||
return (rd.use_game_engine==False) and (rd.engine in self.COMPAT_ENGINES)
|
return (rd.use_game_engine==False) and (rd.engine in self.COMPAT_ENGINES)
|
||||||
|
|
||||||
# Setting panel, use in the scene for now.
|
# Setting panel, use in the scene for now.
|
||||||
@rnaType
|
@rnaType
|
||||||
class RENDER_PT_network_settings(RenderButtonsPanel):
|
class RENDER_PT_network_settings(RenderButtonsPanel):
|
||||||
bl_label = "Network Settings"
|
bl_label = "Network Settings"
|
||||||
COMPAT_ENGINES = {'NET_RENDER'}
|
COMPAT_ENGINES = {'NET_RENDER'}
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
|
||||||
scene = context.scene
|
scene = context.scene
|
||||||
rd = scene.render_data
|
rd = scene.render_data
|
||||||
|
|
||||||
layout.active = True
|
|
||||||
|
|
||||||
split = layout.split()
|
|
||||||
|
|
||||||
col = split.column()
|
|
||||||
|
|
||||||
|
layout.active = True
|
||||||
if scene.network_render.mode in ("RENDER_MASTER", "RENDER_SLAVE"):
|
|
||||||
col.operator("screen.render", text="Start", icon='PLAY').animation = True
|
|
||||||
|
|
||||||
col.prop(scene.network_render, "mode")
|
split = layout.split()
|
||||||
col.prop(scene.network_render, "path")
|
|
||||||
col.prop(scene.network_render, "server_address")
|
|
||||||
col.prop(scene.network_render, "server_port")
|
|
||||||
|
|
||||||
if scene.network_render.mode == "RENDER_MASTER":
|
|
||||||
col.prop(scene.network_render, "server_broadcast")
|
|
||||||
else:
|
|
||||||
col.operator("render.netclientscan", icon='FILE_REFRESH', text="")
|
|
||||||
|
|
||||||
col.operator("render.netclientweb", icon='QUESTION')
|
col = split.column()
|
||||||
|
|
||||||
|
|
||||||
|
if scene.network_render.mode in ("RENDER_MASTER", "RENDER_SLAVE"):
|
||||||
|
col.operator("screen.render", text="Start", icon='PLAY').animation = True
|
||||||
|
|
||||||
|
col.prop(scene.network_render, "mode")
|
||||||
|
col.prop(scene.network_render, "path")
|
||||||
|
col.prop(scene.network_render, "server_address")
|
||||||
|
col.prop(scene.network_render, "server_port")
|
||||||
|
|
||||||
|
if scene.network_render.mode == "RENDER_MASTER":
|
||||||
|
col.prop(scene.network_render, "server_broadcast")
|
||||||
|
else:
|
||||||
|
col.operator("render.netclientscan", icon='FILE_REFRESH', text="")
|
||||||
|
|
||||||
|
col.operator("render.netclientweb", icon='QUESTION')
|
||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class RENDER_PT_network_job(RenderButtonsPanel):
|
class RENDER_PT_network_job(RenderButtonsPanel):
|
||||||
bl_label = "Job Settings"
|
bl_label = "Job Settings"
|
||||||
COMPAT_ENGINES = {'NET_RENDER'}
|
COMPAT_ENGINES = {'NET_RENDER'}
|
||||||
|
|
||||||
def poll(self, context):
|
|
||||||
scene = context.scene
|
|
||||||
return (super().poll(context)
|
|
||||||
and scene.network_render.mode == "RENDER_CLIENT")
|
|
||||||
|
|
||||||
def draw(self, context):
|
def poll(self, context):
|
||||||
layout = self.layout
|
scene = context.scene
|
||||||
|
return (super().poll(context)
|
||||||
|
and scene.network_render.mode == "RENDER_CLIENT")
|
||||||
|
|
||||||
scene = context.scene
|
def draw(self, context):
|
||||||
rd = scene.render_data
|
layout = self.layout
|
||||||
|
|
||||||
layout.active = True
|
scene = context.scene
|
||||||
|
rd = scene.render_data
|
||||||
split = layout.split()
|
|
||||||
|
layout.active = True
|
||||||
col = split.column()
|
|
||||||
if scene.network_render.server_address != "[default]":
|
split = layout.split()
|
||||||
col.operator("render.netclientanim", icon='RENDER_ANIMATION')
|
|
||||||
col.operator("render.netclientsend", icon='FILE_BLEND')
|
col = split.column()
|
||||||
if scene.network_render.job_id:
|
if scene.network_render.server_address != "[default]":
|
||||||
col.operator("screen.render", text="Get Results", icon='RENDER_ANIMATION').animation = True
|
col.operator("render.netclientanim", icon='RENDER_ANIMATION')
|
||||||
col.prop(scene.network_render, "job_name")
|
col.operator("render.netclientsend", icon='FILE_BLEND')
|
||||||
col.prop(scene.network_render, "job_category")
|
if scene.network_render.job_id:
|
||||||
row = col.row()
|
col.operator("screen.render", text="Get Results", icon='RENDER_ANIMATION').animation = True
|
||||||
row.prop(scene.network_render, "priority")
|
col.prop(scene.network_render, "job_name")
|
||||||
row.prop(scene.network_render, "chunks")
|
col.prop(scene.network_render, "job_category")
|
||||||
|
row = col.row()
|
||||||
|
row.prop(scene.network_render, "priority")
|
||||||
|
row.prop(scene.network_render, "chunks")
|
||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class RENDER_PT_network_slaves(RenderButtonsPanel):
|
class RENDER_PT_network_slaves(RenderButtonsPanel):
|
||||||
bl_label = "Slaves Status"
|
bl_label = "Slaves Status"
|
||||||
COMPAT_ENGINES = {'NET_RENDER'}
|
COMPAT_ENGINES = {'NET_RENDER'}
|
||||||
|
|
||||||
def poll(self, context):
|
|
||||||
scene = context.scene
|
|
||||||
return (super().poll(context)
|
|
||||||
and scene.network_render.mode == "RENDER_CLIENT"
|
|
||||||
and scene.network_render.server_address != "[default]")
|
|
||||||
|
|
||||||
def draw(self, context):
|
def poll(self, context):
|
||||||
layout = self.layout
|
scene = context.scene
|
||||||
|
return (super().poll(context)
|
||||||
scene = context.scene
|
and scene.network_render.mode == "RENDER_CLIENT"
|
||||||
netsettings = scene.network_render
|
and scene.network_render.server_address != "[default]")
|
||||||
|
|
||||||
row = layout.row()
|
def draw(self, context):
|
||||||
row.template_list(netsettings, "slaves", netsettings, "active_slave_index", rows=2)
|
layout = self.layout
|
||||||
|
|
||||||
sub = row.column(align=True)
|
scene = context.scene
|
||||||
sub.operator("render.netclientslaves", icon='FILE_REFRESH', text="")
|
netsettings = scene.network_render
|
||||||
sub.operator("render.netclientblacklistslave", icon='ZOOMOUT', text="")
|
|
||||||
|
|
||||||
if len(netrender.slaves) == 0 and len(netsettings.slaves) > 0:
|
|
||||||
while(len(netsettings.slaves) > 0):
|
|
||||||
netsettings.slaves.remove(0)
|
|
||||||
|
|
||||||
if netsettings.active_slave_index >= 0 and len(netsettings.slaves) > 0:
|
|
||||||
layout.separator()
|
|
||||||
|
|
||||||
slave = netrender.slaves[netsettings.active_slave_index]
|
|
||||||
|
|
||||||
layout.label(text="Name: " + slave.name)
|
row = layout.row()
|
||||||
layout.label(text="Address: " + slave.address[0])
|
row.template_list(netsettings, "slaves", netsettings, "active_slave_index", rows=2)
|
||||||
layout.label(text="Seen: " + time.ctime(slave.last_seen))
|
|
||||||
layout.label(text="Stats: " + slave.stats)
|
sub = row.column(align=True)
|
||||||
|
sub.operator("render.netclientslaves", icon='FILE_REFRESH', text="")
|
||||||
|
sub.operator("render.netclientblacklistslave", icon='ZOOMOUT', text="")
|
||||||
|
|
||||||
|
if len(netrender.slaves) == 0 and len(netsettings.slaves) > 0:
|
||||||
|
while(len(netsettings.slaves) > 0):
|
||||||
|
netsettings.slaves.remove(0)
|
||||||
|
|
||||||
|
if netsettings.active_slave_index >= 0 and len(netsettings.slaves) > 0:
|
||||||
|
layout.separator()
|
||||||
|
|
||||||
|
slave = netrender.slaves[netsettings.active_slave_index]
|
||||||
|
|
||||||
|
layout.label(text="Name: " + slave.name)
|
||||||
|
layout.label(text="Address: " + slave.address[0])
|
||||||
|
layout.label(text="Seen: " + time.ctime(slave.last_seen))
|
||||||
|
layout.label(text="Stats: " + slave.stats)
|
||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class RENDER_PT_network_slaves_blacklist(RenderButtonsPanel):
|
class RENDER_PT_network_slaves_blacklist(RenderButtonsPanel):
|
||||||
bl_label = "Slaves Blacklist"
|
bl_label = "Slaves Blacklist"
|
||||||
COMPAT_ENGINES = {'NET_RENDER'}
|
COMPAT_ENGINES = {'NET_RENDER'}
|
||||||
|
|
||||||
def poll(self, context):
|
|
||||||
scene = context.scene
|
|
||||||
return (super().poll(context)
|
|
||||||
and scene.network_render.mode == "RENDER_CLIENT"
|
|
||||||
and scene.network_render.server_address != "[default]")
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
layout = self.layout
|
|
||||||
|
|
||||||
scene = context.scene
|
|
||||||
netsettings = scene.network_render
|
|
||||||
|
|
||||||
row = layout.row()
|
def poll(self, context):
|
||||||
row.template_list(netsettings, "slaves_blacklist", netsettings, "active_blacklisted_slave_index", rows=2)
|
scene = context.scene
|
||||||
|
return (super().poll(context)
|
||||||
|
and scene.network_render.mode == "RENDER_CLIENT"
|
||||||
|
and scene.network_render.server_address != "[default]")
|
||||||
|
|
||||||
sub = row.column(align=True)
|
def draw(self, context):
|
||||||
sub.operator("render.netclientwhitelistslave", icon='ZOOMOUT', text="")
|
layout = self.layout
|
||||||
|
|
||||||
if len(netrender.blacklist) == 0 and len(netsettings.slaves_blacklist) > 0:
|
scene = context.scene
|
||||||
while(len(netsettings.slaves_blacklist) > 0):
|
netsettings = scene.network_render
|
||||||
netsettings.slaves_blacklist.remove(0)
|
|
||||||
|
|
||||||
if netsettings.active_blacklisted_slave_index >= 0 and len(netsettings.slaves_blacklist) > 0:
|
|
||||||
layout.separator()
|
|
||||||
|
|
||||||
slave = netrender.blacklist[netsettings.active_blacklisted_slave_index]
|
|
||||||
|
|
||||||
layout.label(text="Name: " + slave.name)
|
row = layout.row()
|
||||||
layout.label(text="Address: " + slave.address[0])
|
row.template_list(netsettings, "slaves_blacklist", netsettings, "active_blacklisted_slave_index", rows=2)
|
||||||
layout.label(text="Seen: " + time.ctime(slave.last_seen))
|
|
||||||
layout.label(text="Stats: " + slave.stats)
|
sub = row.column(align=True)
|
||||||
|
sub.operator("render.netclientwhitelistslave", icon='ZOOMOUT', text="")
|
||||||
|
|
||||||
|
if len(netrender.blacklist) == 0 and len(netsettings.slaves_blacklist) > 0:
|
||||||
|
while(len(netsettings.slaves_blacklist) > 0):
|
||||||
|
netsettings.slaves_blacklist.remove(0)
|
||||||
|
|
||||||
|
if netsettings.active_blacklisted_slave_index >= 0 and len(netsettings.slaves_blacklist) > 0:
|
||||||
|
layout.separator()
|
||||||
|
|
||||||
|
slave = netrender.blacklist[netsettings.active_blacklisted_slave_index]
|
||||||
|
|
||||||
|
layout.label(text="Name: " + slave.name)
|
||||||
|
layout.label(text="Address: " + slave.address[0])
|
||||||
|
layout.label(text="Seen: " + time.ctime(slave.last_seen))
|
||||||
|
layout.label(text="Stats: " + slave.stats)
|
||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class RENDER_PT_network_jobs(RenderButtonsPanel):
|
class RENDER_PT_network_jobs(RenderButtonsPanel):
|
||||||
bl_label = "Jobs"
|
bl_label = "Jobs"
|
||||||
COMPAT_ENGINES = {'NET_RENDER'}
|
COMPAT_ENGINES = {'NET_RENDER'}
|
||||||
|
|
||||||
def poll(self, context):
|
|
||||||
scene = context.scene
|
|
||||||
return (super().poll(context)
|
|
||||||
and scene.network_render.mode == "RENDER_CLIENT"
|
|
||||||
and scene.network_render.server_address != "[default]")
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
layout = self.layout
|
|
||||||
|
|
||||||
scene = context.scene
|
|
||||||
netsettings = scene.network_render
|
|
||||||
|
|
||||||
row = layout.row()
|
def poll(self, context):
|
||||||
row.template_list(netsettings, "jobs", netsettings, "active_job_index", rows=2)
|
scene = context.scene
|
||||||
|
return (super().poll(context)
|
||||||
|
and scene.network_render.mode == "RENDER_CLIENT"
|
||||||
|
and scene.network_render.server_address != "[default]")
|
||||||
|
|
||||||
sub = row.column(align=True)
|
def draw(self, context):
|
||||||
sub.operator("render.netclientstatus", icon='FILE_REFRESH', text="")
|
layout = self.layout
|
||||||
sub.operator("render.netclientcancel", icon='ZOOMOUT', text="")
|
|
||||||
sub.operator("render.netclientcancelall", icon='PANEL_CLOSE', text="")
|
|
||||||
sub.operator("render.netclientdownload", icon='RENDER_ANIMATION', text="")
|
|
||||||
|
|
||||||
if len(netrender.jobs) == 0 and len(netsettings.jobs) > 0:
|
scene = context.scene
|
||||||
while(len(netsettings.jobs) > 0):
|
netsettings = scene.network_render
|
||||||
netsettings.jobs.remove(0)
|
|
||||||
|
|
||||||
if netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0:
|
|
||||||
layout.separator()
|
|
||||||
|
|
||||||
job = netrender.jobs[netsettings.active_job_index]
|
|
||||||
|
|
||||||
layout.label(text="Name: %s" % job.name)
|
row = layout.row()
|
||||||
layout.label(text="Length: %04i" % len(job))
|
row.template_list(netsettings, "jobs", netsettings, "active_job_index", rows=2)
|
||||||
layout.label(text="Done: %04i" % job.results[DONE])
|
|
||||||
layout.label(text="Error: %04i" % job.results[ERROR])
|
sub = row.column(align=True)
|
||||||
|
sub.operator("render.netclientstatus", icon='FILE_REFRESH', text="")
|
||||||
|
sub.operator("render.netclientcancel", icon='ZOOMOUT', text="")
|
||||||
|
sub.operator("render.netclientcancelall", icon='PANEL_CLOSE', text="")
|
||||||
|
sub.operator("render.netclientdownload", icon='RENDER_ANIMATION', text="")
|
||||||
|
|
||||||
|
if len(netrender.jobs) == 0 and len(netsettings.jobs) > 0:
|
||||||
|
while(len(netsettings.jobs) > 0):
|
||||||
|
netsettings.jobs.remove(0)
|
||||||
|
|
||||||
|
if netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0:
|
||||||
|
layout.separator()
|
||||||
|
|
||||||
|
job = netrender.jobs[netsettings.active_job_index]
|
||||||
|
|
||||||
|
layout.label(text="Name: %s" % job.name)
|
||||||
|
layout.label(text="Length: %04i" % len(job))
|
||||||
|
layout.label(text="Done: %04i" % job.results[DONE])
|
||||||
|
layout.label(text="Error: %04i" % job.results[ERROR])
|
||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class NetRenderSettings(bpy.types.IDPropertyGroup):
|
class NetRenderSettings(bpy.types.IDPropertyGroup):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class NetRenderSlave(bpy.types.IDPropertyGroup):
|
class NetRenderSlave(bpy.types.IDPropertyGroup):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@rnaType
|
@rnaType
|
||||||
class NetRenderJob(bpy.types.IDPropertyGroup):
|
class NetRenderJob(bpy.types.IDPropertyGroup):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
bpy.types.Scene.PointerProperty(attr="network_render", type=NetRenderSettings, name="Network Render", description="Network Render Settings")
|
bpy.types.Scene.PointerProperty(attr="network_render", type=NetRenderSettings, name="Network Render", description="Network Render Settings")
|
||||||
|
|
||||||
NetRenderSettings.StringProperty( attr="server_address",
|
NetRenderSettings.StringProperty( attr="server_address",
|
||||||
name="Server address",
|
name="Server address",
|
||||||
description="IP or name of the master render server",
|
description="IP or name of the master render server",
|
||||||
maxlen = 128,
|
maxlen = 128,
|
||||||
default = "[default]")
|
default = "[default]")
|
||||||
|
|
||||||
NetRenderSettings.IntProperty( attr="server_port",
|
NetRenderSettings.IntProperty( attr="server_port",
|
||||||
name="Server port",
|
name="Server port",
|
||||||
description="port of the master render server",
|
description="port of the master render server",
|
||||||
default = 8000,
|
default = 8000,
|
||||||
min=1,
|
min=1,
|
||||||
max=65535)
|
max=65535)
|
||||||
|
|
||||||
NetRenderSettings.BoolProperty( attr="server_broadcast",
|
NetRenderSettings.BoolProperty( attr="server_broadcast",
|
||||||
name="Broadcast server address",
|
name="Broadcast server address",
|
||||||
description="broadcast server address on local network",
|
description="broadcast server address on local network",
|
||||||
default = True)
|
default = True)
|
||||||
|
|
||||||
default_path = os.environ.get("TEMP", None)
|
default_path = os.environ.get("TEMP", None)
|
||||||
|
|
||||||
if not default_path:
|
if not default_path:
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
default_path = "c:/tmp/"
|
default_path = "c:/tmp/"
|
||||||
else:
|
else:
|
||||||
default_path = "/tmp/"
|
default_path = "/tmp/"
|
||||||
elif not default_path.endswith(os.sep):
|
elif not default_path.endswith(os.sep):
|
||||||
default_path += os.sep
|
default_path += os.sep
|
||||||
|
|
||||||
NetRenderSettings.StringProperty( attr="path",
|
NetRenderSettings.StringProperty( attr="path",
|
||||||
name="Path",
|
name="Path",
|
||||||
description="Path for temporary files",
|
description="Path for temporary files",
|
||||||
maxlen = 128,
|
maxlen = 128,
|
||||||
default = default_path)
|
default = default_path)
|
||||||
|
|
||||||
NetRenderSettings.StringProperty( attr="job_name",
|
NetRenderSettings.StringProperty( attr="job_name",
|
||||||
name="Job name",
|
name="Job name",
|
||||||
description="Name of the job",
|
description="Name of the job",
|
||||||
maxlen = 128,
|
maxlen = 128,
|
||||||
default = "[default]")
|
default = "[default]")
|
||||||
|
|
||||||
NetRenderSettings.StringProperty( attr="job_category",
|
NetRenderSettings.StringProperty( attr="job_category",
|
||||||
name="Job category",
|
name="Job category",
|
||||||
description="Category of the job",
|
description="Category of the job",
|
||||||
maxlen = 128,
|
maxlen = 128,
|
||||||
default = "")
|
default = "")
|
||||||
|
|
||||||
NetRenderSettings.IntProperty( attr="chunks",
|
NetRenderSettings.IntProperty( attr="chunks",
|
||||||
name="Chunks",
|
name="Chunks",
|
||||||
description="Number of frame to dispatch to each slave in one chunk",
|
description="Number of frame to dispatch to each slave in one chunk",
|
||||||
default = 5,
|
default = 5,
|
||||||
min=1,
|
min=1,
|
||||||
max=65535)
|
max=65535)
|
||||||
|
|
||||||
NetRenderSettings.IntProperty( attr="priority",
|
NetRenderSettings.IntProperty( attr="priority",
|
||||||
name="Priority",
|
name="Priority",
|
||||||
description="Priority of the job",
|
description="Priority of the job",
|
||||||
default = 1,
|
default = 1,
|
||||||
min=1,
|
min=1,
|
||||||
max=10)
|
max=10)
|
||||||
|
|
||||||
NetRenderSettings.StringProperty( attr="job_id",
|
NetRenderSettings.StringProperty( attr="job_id",
|
||||||
name="Network job id",
|
name="Network job id",
|
||||||
description="id of the last sent render job",
|
description="id of the last sent render job",
|
||||||
maxlen = 64,
|
maxlen = 64,
|
||||||
default = "")
|
default = "")
|
||||||
|
|
||||||
NetRenderSettings.IntProperty( attr="active_slave_index",
|
NetRenderSettings.IntProperty( attr="active_slave_index",
|
||||||
name="Index of the active slave",
|
name="Index of the active slave",
|
||||||
description="",
|
description="",
|
||||||
default = -1,
|
default = -1,
|
||||||
min= -1,
|
min= -1,
|
||||||
max=65535)
|
max=65535)
|
||||||
|
|
||||||
NetRenderSettings.IntProperty( attr="active_blacklisted_slave_index",
|
NetRenderSettings.IntProperty( attr="active_blacklisted_slave_index",
|
||||||
name="Index of the active slave",
|
name="Index of the active slave",
|
||||||
description="",
|
description="",
|
||||||
default = -1,
|
default = -1,
|
||||||
min= -1,
|
min= -1,
|
||||||
max=65535)
|
max=65535)
|
||||||
|
|
||||||
NetRenderSettings.IntProperty( attr="active_job_index",
|
NetRenderSettings.IntProperty( attr="active_job_index",
|
||||||
name="Index of the active job",
|
name="Index of the active job",
|
||||||
description="",
|
description="",
|
||||||
default = -1,
|
default = -1,
|
||||||
min= -1,
|
min= -1,
|
||||||
max=65535)
|
max=65535)
|
||||||
|
|
||||||
NetRenderSettings.EnumProperty(attr="mode",
|
NetRenderSettings.EnumProperty(attr="mode",
|
||||||
items=(
|
items=(
|
||||||
("RENDER_CLIENT", "Client", "Act as render client"),
|
("RENDER_CLIENT", "Client", "Act as render client"),
|
||||||
("RENDER_MASTER", "Master", "Act as render master"),
|
("RENDER_MASTER", "Master", "Act as render master"),
|
||||||
("RENDER_SLAVE", "Slave", "Act as render slave"),
|
("RENDER_SLAVE", "Slave", "Act as render slave"),
|
||||||
),
|
),
|
||||||
name="Network mode",
|
name="Network mode",
|
||||||
description="Mode of operation of this instance",
|
description="Mode of operation of this instance",
|
||||||
default="RENDER_CLIENT")
|
default="RENDER_CLIENT")
|
||||||
|
|
||||||
NetRenderSettings.CollectionProperty(attr="slaves", type=NetRenderSlave, name="Slaves", description="")
|
NetRenderSettings.CollectionProperty(attr="slaves", type=NetRenderSlave, name="Slaves", description="")
|
||||||
NetRenderSettings.CollectionProperty(attr="slaves_blacklist", type=NetRenderSlave, name="Slaves Blacklist", description="")
|
NetRenderSettings.CollectionProperty(attr="slaves_blacklist", type=NetRenderSlave, name="Slaves Blacklist", description="")
|
||||||
NetRenderSettings.CollectionProperty(attr="jobs", type=NetRenderJob, name="Job List", description="")
|
NetRenderSettings.CollectionProperty(attr="jobs", type=NetRenderJob, name="Job List", description="")
|
||||||
|
|
||||||
NetRenderSlave.StringProperty( attr="name",
|
NetRenderSlave.StringProperty( attr="name",
|
||||||
name="Name of the slave",
|
name="Name of the slave",
|
||||||
description="",
|
description="",
|
||||||
maxlen = 64,
|
maxlen = 64,
|
||||||
default = "")
|
default = "")
|
||||||
|
|
||||||
NetRenderJob.StringProperty( attr="name",
|
NetRenderJob.StringProperty( attr="name",
|
||||||
name="Name of the job",
|
name="Name of the job",
|
||||||
description="",
|
description="",
|
||||||
maxlen = 128,
|
maxlen = 128,
|
||||||
default = "")
|
default = "")
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
# as published by the Free Software Foundation; either version 2
|
# as published by the Free Software Foundation; either version 2
|
||||||
# of the License, or (at your option) any later version.
|
# of the License, or (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
@@ -51,22 +51,22 @@ DONE = 2
|
|||||||
ERROR = 3
|
ERROR = 3
|
||||||
|
|
||||||
FRAME_STATUS_TEXT = {
|
FRAME_STATUS_TEXT = {
|
||||||
QUEUED: "Queued",
|
QUEUED: "Queued",
|
||||||
DISPATCHED: "Dispatched",
|
DISPATCHED: "Dispatched",
|
||||||
DONE: "Done",
|
DONE: "Done",
|
||||||
ERROR: "Error"
|
ERROR: "Error"
|
||||||
}
|
}
|
||||||
|
|
||||||
def rnaType(rna_type):
|
def rnaType(rna_type):
|
||||||
if bpy: bpy.types.register(rna_type)
|
if bpy: bpy.types.register(rna_type)
|
||||||
return rna_type
|
return rna_type
|
||||||
|
|
||||||
def reporting(report, message, errorType = None):
|
def reporting(report, message, errorType = None):
|
||||||
if errorType:
|
if errorType:
|
||||||
t = 'ERROR'
|
t = 'ERROR'
|
||||||
else:
|
else:
|
||||||
t = 'INFO'
|
t = 'INFO'
|
||||||
|
|
||||||
if report:
|
if report:
|
||||||
report(t, message)
|
report(t, message)
|
||||||
return None
|
return None
|
||||||
@@ -82,14 +82,14 @@ def clientScan(report = None):
|
|||||||
s.settimeout(30)
|
s.settimeout(30)
|
||||||
|
|
||||||
s.bind(('', 8000))
|
s.bind(('', 8000))
|
||||||
|
|
||||||
buf, address = s.recvfrom(64)
|
buf, address = s.recvfrom(64)
|
||||||
|
|
||||||
address = address[0]
|
address = address[0]
|
||||||
port = int(str(buf, encoding='utf8'))
|
port = int(str(buf, encoding='utf8'))
|
||||||
|
|
||||||
reporting(report, "Master server found")
|
reporting(report, "Master server found")
|
||||||
|
|
||||||
return (address, port)
|
return (address, port)
|
||||||
except socket.timeout:
|
except socket.timeout:
|
||||||
reporting(report, "No master server on network", IOError)
|
reporting(report, "No master server on network", IOError)
|
||||||
@@ -105,10 +105,10 @@ def clientConnection(address, port, report = None):
|
|||||||
address, port = clientScan()
|
address, port = clientScan()
|
||||||
if address == "":
|
if address == "":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn = http.client.HTTPConnection(address, port)
|
conn = http.client.HTTPConnection(address, port)
|
||||||
|
|
||||||
if conn:
|
if conn:
|
||||||
if clientVerifyVersion(conn):
|
if clientVerifyVersion(conn):
|
||||||
return conn
|
return conn
|
||||||
@@ -123,21 +123,21 @@ def clientConnection(address, port, report = None):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
def clientVerifyVersion(conn):
|
def clientVerifyVersion(conn):
|
||||||
conn.request("GET", "/version")
|
conn.request("GET", "/version")
|
||||||
response = conn.getresponse()
|
response = conn.getresponse()
|
||||||
|
|
||||||
if response.status != http.client.OK:
|
if response.status != http.client.OK:
|
||||||
conn.close()
|
conn.close()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
server_version = response.read()
|
server_version = response.read()
|
||||||
|
|
||||||
if server_version != VERSION:
|
if server_version != VERSION:
|
||||||
print("Incorrect server version!")
|
print("Incorrect server version!")
|
||||||
print("expected", str(VERSION, encoding='utf8'), "received", str(server_version, encoding='utf8'))
|
print("expected", str(VERSION, encoding='utf8'), "received", str(server_version, encoding='utf8'))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def fileURL(job_id, file_index):
|
def fileURL(job_id, file_index):
|
||||||
return "/file_%s_%i" % (job_id, file_index)
|
return "/file_%s_%i" % (job_id, file_index)
|
||||||
@@ -152,20 +152,20 @@ def cancelURL(job_id):
|
|||||||
return "/cancel_%s" % (job_id)
|
return "/cancel_%s" % (job_id)
|
||||||
|
|
||||||
def prefixPath(prefix_directory, file_path, prefix_path):
|
def prefixPath(prefix_directory, file_path, prefix_path):
|
||||||
if os.path.isabs(file_path):
|
if os.path.isabs(file_path):
|
||||||
# if an absolute path, make sure path exists, if it doesn't, use relative local path
|
# if an absolute path, make sure path exists, if it doesn't, use relative local path
|
||||||
full_path = file_path
|
full_path = file_path
|
||||||
if not os.path.exists(full_path):
|
if not os.path.exists(full_path):
|
||||||
p, n = os.path.split(full_path)
|
p, n = os.path.split(full_path)
|
||||||
|
|
||||||
if prefix_path and p.startswith(prefix_path):
|
if prefix_path and p.startswith(prefix_path):
|
||||||
directory = prefix_directory + p[len(prefix_path):]
|
directory = prefix_directory + p[len(prefix_path):]
|
||||||
full_path = directory + os.sep + n
|
full_path = directory + os.sep + n
|
||||||
if not os.path.exists(directory):
|
if not os.path.exists(directory):
|
||||||
os.mkdir(directory)
|
os.mkdir(directory)
|
||||||
else:
|
else:
|
||||||
full_path = prefix_directory + n
|
full_path = prefix_directory + n
|
||||||
else:
|
else:
|
||||||
full_path = prefix_directory + file_path
|
full_path = prefix_directory + file_path
|
||||||
|
|
||||||
return full_path
|
return full_path
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ bpy.context.scene.render_data.resolution_percentage = 100
|
|||||||
bpy.context.scene.render_data.pixel_aspect_x = 1
|
bpy.context.scene.render_data.pixel_aspect_x = 1
|
||||||
bpy.context.scene.render_data.pixel_aspect_y = 1
|
bpy.context.scene.render_data.pixel_aspect_y = 1
|
||||||
bpy.context.scene.render_data.fps = 24
|
bpy.context.scene.render_data.fps = 24
|
||||||
bpy.context.scene.render_data.fps_base = 1
|
bpy.context.scene.render_data.fps_base = 1
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ bpy.context.scene.render_data.resolution_percentage = 100
|
|||||||
bpy.context.scene.render_data.pixel_aspect_x = 1
|
bpy.context.scene.render_data.pixel_aspect_x = 1
|
||||||
bpy.context.scene.render_data.pixel_aspect_y = 1
|
bpy.context.scene.render_data.pixel_aspect_y = 1
|
||||||
bpy.context.scene.render_data.fps = 24
|
bpy.context.scene.render_data.fps = 24
|
||||||
bpy.context.scene.render_data.fps_base = 1
|
bpy.context.scene.render_data.fps_base = 1
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ bpy.context.scene.render_data.resolution_percentage = 100
|
|||||||
bpy.context.scene.render_data.pixel_aspect_x = 54
|
bpy.context.scene.render_data.pixel_aspect_x = 54
|
||||||
bpy.context.scene.render_data.pixel_aspect_y = 51
|
bpy.context.scene.render_data.pixel_aspect_y = 51
|
||||||
bpy.context.scene.render_data.fps = 25
|
bpy.context.scene.render_data.fps = 25
|
||||||
bpy.context.scene.render_data.fps_base = 1
|
bpy.context.scene.render_data.fps_base = 1
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ bpy.context.scene.render_data.resolution_percentage = 100
|
|||||||
bpy.context.scene.render_data.pixel_aspect_x = 64
|
bpy.context.scene.render_data.pixel_aspect_x = 64
|
||||||
bpy.context.scene.render_data.pixel_aspect_y = 45
|
bpy.context.scene.render_data.pixel_aspect_y = 45
|
||||||
bpy.context.scene.render_data.fps = 25
|
bpy.context.scene.render_data.fps = 25
|
||||||
bpy.context.scene.render_data.fps_base = 1
|
bpy.context.scene.render_data.fps_base = 1
|
||||||
|
|||||||
@@ -1476,13 +1476,13 @@ class USERPREF_PT_input(bpy.types.Panel):
|
|||||||
|
|
||||||
for km in kc.keymaps:
|
for km in kc.keymaps:
|
||||||
filtered_items = [kmi for kmi in km.items if filter in kmi.name.lower()]
|
filtered_items = [kmi for kmi in km.items if filter in kmi.name.lower()]
|
||||||
|
|
||||||
if len(filtered_items) != 0:
|
if len(filtered_items) != 0:
|
||||||
km = km.active()
|
km = km.active()
|
||||||
|
|
||||||
layout.set_context_pointer("keymap", km)
|
layout.set_context_pointer("keymap", km)
|
||||||
col = layout.column()
|
col = layout.column()
|
||||||
|
|
||||||
row = col.row()
|
row = col.row()
|
||||||
row.label(text=km.name, icon="DOT")
|
row.label(text=km.name, icon="DOT")
|
||||||
|
|
||||||
@@ -1496,7 +1496,7 @@ class USERPREF_PT_input(bpy.types.Panel):
|
|||||||
|
|
||||||
for kmi in filtered_items:
|
for kmi in filtered_items:
|
||||||
self.draw_kmi(kc, km, kmi, col, 1)
|
self.draw_kmi(kc, km, kmi, col, 1)
|
||||||
|
|
||||||
# "Add New" at end of keymap item list
|
# "Add New" at end of keymap item list
|
||||||
col = self.indented_layout(layout, 1)
|
col = self.indented_layout(layout, 1)
|
||||||
subcol = col.split(percentage=0.2).column()
|
subcol = col.split(percentage=0.2).column()
|
||||||
@@ -1813,18 +1813,18 @@ class WM_OT_keyitem_add(bpy.types.Operator):
|
|||||||
wm = context.manager
|
wm = context.manager
|
||||||
km = context.keymap
|
km = context.keymap
|
||||||
kc = wm.default_keyconfig
|
kc = wm.default_keyconfig
|
||||||
|
|
||||||
if km.modal:
|
if km.modal:
|
||||||
km.add_modal_item("", 'A', 'PRESS') # kmi
|
km.add_modal_item("", 'A', 'PRESS') # kmi
|
||||||
else:
|
else:
|
||||||
km.add_item("none", 'A', 'PRESS') # kmi
|
km.add_item("none", 'A', 'PRESS') # kmi
|
||||||
|
|
||||||
# clear filter and expand keymap so we can see the newly added item
|
# clear filter and expand keymap so we can see the newly added item
|
||||||
if kc.filter != '':
|
if kc.filter != '':
|
||||||
kc.filter = ''
|
kc.filter = ''
|
||||||
km.items_expanded = True
|
km.items_expanded = True
|
||||||
km.children_expanded = True
|
km.children_expanded = True
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -498,14 +498,14 @@ class VIEW3D_PT_tools_brush(PaintPanel):
|
|||||||
if not context.particle_edit_object:
|
if not context.particle_edit_object:
|
||||||
col = layout.split().column()
|
col = layout.split().column()
|
||||||
row = col.row()
|
row = col.row()
|
||||||
|
|
||||||
if context.sculpt_object and brush:
|
if context.sculpt_object and brush:
|
||||||
defaulttools = 8
|
defaulttools = 8
|
||||||
elif context.texture_paint_object and brush:
|
elif context.texture_paint_object and brush:
|
||||||
defaulttools = 4
|
defaulttools = 4
|
||||||
else:
|
else:
|
||||||
defaulttools = 2
|
defaulttools = 2
|
||||||
|
|
||||||
row.template_list(settings, "brushes", settings, "active_brush_index", rows=2, maxrows=defaulttools)
|
row.template_list(settings, "brushes", settings, "active_brush_index", rows=2, maxrows=defaulttools)
|
||||||
|
|
||||||
col.template_ID(settings, "brush", new="brush.add")
|
col.template_ID(settings, "brush", new="brush.add")
|
||||||
@@ -539,7 +539,7 @@ class VIEW3D_PT_tools_brush(PaintPanel):
|
|||||||
elif context.sculpt_object and brush:
|
elif context.sculpt_object and brush:
|
||||||
col = layout.column()
|
col = layout.column()
|
||||||
col.separator()
|
col.separator()
|
||||||
|
|
||||||
row = col.row(align=True)
|
row = col.row(align=True)
|
||||||
row.prop(brush, "size", slider=True)
|
row.prop(brush, "size", slider=True)
|
||||||
row.prop(brush, "use_size_pressure", toggle=True, text="")
|
row.prop(brush, "use_size_pressure", toggle=True, text="")
|
||||||
@@ -642,7 +642,7 @@ class VIEW3D_PT_tools_brush_tool(PaintPanel):
|
|||||||
sculpt = context.sculpt_object
|
sculpt = context.sculpt_object
|
||||||
|
|
||||||
col = layout.column(align=True)
|
col = layout.column(align=True)
|
||||||
|
|
||||||
if context.sculpt_object:
|
if context.sculpt_object:
|
||||||
col.prop(brush, "sculpt_tool", expand=True)
|
col.prop(brush, "sculpt_tool", expand=True)
|
||||||
elif context.texture_paint_object:
|
elif context.texture_paint_object:
|
||||||
|
|||||||
Reference in New Issue
Block a user