Merge pull request #2 from blw1138/adaptive_renderer_args

Adaptive renderer args
This commit is contained in:
2023-05-22 16:11:51 -05:00
committed by GitHub
6 changed files with 69 additions and 24 deletions

View File

@@ -33,6 +33,7 @@ class RenderJob:
self.worker = RenderWorkerFactory.create_worker(renderer, input_path, output_path, args) self.worker = RenderWorkerFactory.create_worker(renderer, input_path, output_path, args)
self.worker.log_path = os.path.join(os.path.dirname(input_path), self.name + '.log') self.worker.log_path = os.path.join(os.path.dirname(input_path), self.name + '.log')
self.worker.validate()
self.file_hash = None self.file_hash = None
threading.Thread(target=self.__get_file_hash).start() # get file hash on bg thread threading.Thread(target=self.__get_file_hash).start() # get file hash on bg thread

View File

@@ -47,7 +47,7 @@ class AERenderWorker(BaseRenderWorker):
logging.error(f'Failed to get {cls.renderer} version: {e}') logging.error(f'Failed to get {cls.renderer} version: {e}')
return version return version
def _generate_subprocess(self): def generate_worker_subprocess(self):
if os.path.exists('nexrender-cli-macos'): if os.path.exists('nexrender-cli-macos'):
logging.info('nexrender found') logging.info('nexrender found')

View File

@@ -21,7 +21,8 @@ class BlenderRenderWorker(BaseRenderWorker):
self.engine = self.args.get('engine', 'BLENDER_EEVEE').upper() self.engine = self.args.get('engine', 'BLENDER_EEVEE').upper()
self.export_format = self.args.get('export_format', None) or 'JPEG' self.export_format = self.args.get('export_format', None) or 'JPEG'
self.camera = self.args.get('camera', None) self.camera = self.args.get('camera', None)
self.render_all_frames = self.args.get('render_all_frames', False) self.render_all_frames = self.args.get('render_all_frames', False) or \
'-a' in (self.args.get('raw', None) or "").split(' ')
self.frame_to_render = 0 self.frame_to_render = 0
# Stats # Stats
@@ -45,7 +46,7 @@ class BlenderRenderWorker(BaseRenderWorker):
logging.error(f'Failed to get {cls.renderer} version: {e}') logging.error(f'Failed to get {cls.renderer} version: {e}')
return version return version
def _generate_subprocess(self): def generate_worker_subprocess(self):
cmd = [self.renderer_path()] cmd = [self.renderer_path()]
if self.args.get('background', True): # optionally run render not in background if self.args.get('background', True): # optionally run render not in background
@@ -61,9 +62,9 @@ class BlenderRenderWorker(BaseRenderWorker):
cmd.extend(['-a'] if self.render_all_frames else ['-f', str(self.frame_to_render)]) cmd.extend(['-a'] if self.render_all_frames else ['-f', str(self.frame_to_render)])
# Convert raw args from string if available # Convert raw args from string if available
raw_args = self.args.get('raw', None) raw_args = self.get_raw_args()
if raw_args: if raw_args:
cmd.extend(raw_args.split(' ')) cmd.extend(raw_args)
return cmd return cmd

View File

@@ -35,7 +35,7 @@ class FFMPEGRenderWorker(BaseRenderWorker):
logger.error("Failed to get FFMPEG version: {}".format(e)) logger.error("Failed to get FFMPEG version: {}".format(e))
return version return version
def _generate_subprocess(self): def generate_worker_subprocess(self):
cmd = [self.renderer_path(), '-y', '-stats', '-i', self.input_path] cmd = [self.renderer_path(), '-y', '-stats', '-i', self.input_path]
@@ -44,9 +44,9 @@ class FFMPEGRenderWorker(BaseRenderWorker):
cmd.extend(['-vf', f"scale={self.args['x_resolution']}:{self.args['y_resolution']}"]) cmd.extend(['-vf', f"scale={self.args['x_resolution']}:{self.args['y_resolution']}"])
# Convert raw args from string if available # Convert raw args from string if available
raw_args = self.args.get('raw', None) raw_args = self.get_raw_args()
if raw_args: if raw_args:
cmd.extend(raw_args.split(' ')) cmd.extend(raw_args)
# Close with output path # Close with output path
cmd.append(self.output_path) cmd.append(self.output_path)

View File

@@ -92,8 +92,32 @@ class BaseRenderWorker(object):
logging.exception(e) logging.exception(e)
return path return path
def _generate_subprocess(self): def validate(self):
raise NotImplementedError("_generate_subprocess not implemented") if not os.path.exists(self.input_path):
raise FileNotFoundError(f"Cannot find input path: {self.input_path}")
self.generate_subprocess()
def generate_subprocess(self):
# Convert raw args from string if available and catch conflicts
generated_args = self.generate_worker_subprocess()
generated_args_flags = [x for x in generated_args if x.startswith('-')]
if len(generated_args_flags) != len(set(generated_args_flags)):
msg = "Cannot generate subprocess - Multiple arg conflicts detected"
logger.error(msg)
logger.debug(f"Generated args for subprocess: {generated_args}")
raise ValueError(msg)
return generated_args
def get_raw_args(self):
raw_args_string = self.args.get('raw', None)
raw_args = None
if raw_args_string:
import shlex
raw_args = shlex.split(raw_args_string)
return raw_args
def generate_worker_subprocess(self):
raise NotImplementedError("generate_worker_subprocess not implemented")
def start(self): def start(self):
@@ -134,7 +158,7 @@ class BaseRenderWorker(object):
logger.info('Attempt #{} failed. Starting attempt #{}'.format(self.failed_attempts, self.failed_attempts + 1)) logger.info('Attempt #{} failed. Starting attempt #{}'.format(self.failed_attempts, self.failed_attempts + 1))
# Start process and get updates # Start process and get updates
subprocess_cmds = self._generate_subprocess() subprocess_cmds = self.generate_subprocess()
logger.debug("Renderer commands generated - {}".format(" ".join(subprocess_cmds))) logger.debug("Renderer commands generated - {}".format(" ".join(subprocess_cmds)))
self.__process = subprocess.Popen(subprocess_cmds, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, self.__process = subprocess.Popen(subprocess_cmds, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
universal_newlines=False) universal_newlines=False)

View File

@@ -16,7 +16,7 @@ from lib.utilities.server_helper import post_job_to_server
logger = logging.getLogger() logger = logging.getLogger()
prefs_name = 'config/.scheduler_prefs' prefs_name = 'config/.scheduler_prefs'
label_width = 7 label_width = 9
header_padding = 6 header_padding = 6
server_setup_timeout = 5 server_setup_timeout = 5
@@ -137,7 +137,9 @@ class ScheduleJob(Frame):
self.blender_multiple_cameras = BooleanVar(value=False) self.blender_multiple_cameras = BooleanVar(value=False)
self.blender_cameras_list = None self.blender_cameras_list = None
# Submit Button # Custom Args / Submit Button
self.custom_args_frame = None
self.custom_args_entry = None
self.submit_frame = None self.submit_frame = None
if os.path.exists(prefs_name): if os.path.exists(prefs_name):
@@ -145,9 +147,12 @@ class ScheduleJob(Frame):
hostname = file.read() hostname = file.read()
server_data = request_data(hostname, 'status', timeout=server_setup_timeout) server_data = request_data(hostname, 'status', timeout=server_setup_timeout)
if server_data: if server_data:
self.server_hostname = hostname self.set_hostname(hostname)
self.server_button.configure(text=hostname)
if not self.server_hostname: if not self.server_hostname:
server_data = request_data('localhost', 'status', timeout=server_setup_timeout)
if server_data:
self.set_hostname(server_data['host_name'])
else:
self.request_new_hostname() self.request_new_hostname()
self.fetch_server_data() self.fetch_server_data()
@@ -179,11 +184,12 @@ class ScheduleJob(Frame):
messagebox.showerror("Cannot connect", f"Cannot connect to server \"{user_hostname_input}\"") messagebox.showerror("Cannot connect", f"Cannot connect to server \"{user_hostname_input}\"")
else: else:
hostname = user_hostname_input hostname = user_hostname_input
self.set_hostname(hostname)
def set_hostname(self, hostname):
self.server_hostname = hostname self.server_hostname = hostname
self.server_button.configure(text=self.server_hostname) self.server_button.configure(text=self.server_hostname)
with open(prefs_name, 'w') as file: # save to prefs
# save to prefs
with open(prefs_name, 'w') as file:
file.write(hostname) file.write(hostname)
def choose_file_button(self): def choose_file_button(self):
@@ -216,6 +222,7 @@ class ScheduleJob(Frame):
if renderer == 'blender': if renderer == 'blender':
self.draw_blender_settings() self.draw_blender_settings()
self.draw_custom_args()
self.draw_submit_button() self.draw_submit_button()
if self.renderer_info.get(renderer, {}).get('supported_export_formats', None): if self.renderer_info.get(renderer, {}).get('supported_export_formats', None):
@@ -228,6 +235,15 @@ class ScheduleJob(Frame):
self.output_format['values'] = [] self.output_format['values'] = []
self.output_format['state'] = DISABLED self.output_format['state'] = DISABLED
def draw_custom_args(self):
if hasattr(self, 'custom_args_frame') and self.custom_args_frame:
self.custom_args_frame.forget()
self.custom_args_frame = Frame(self)
self.custom_args_frame.pack(side=TOP, fill=X, expand=False)
Label(self.custom_args_frame, text="Custom Args", width=label_width).pack(side=LEFT, padx=5, pady=5)
self.custom_args_entry = Entry(self.custom_args_frame)
self.custom_args_entry.pack(side=TOP, padx=5, expand=True, fill=X)
def draw_submit_button(self): def draw_submit_button(self):
if hasattr(self, 'submit_frame') and self.submit_frame: if hasattr(self, 'submit_frame') and self.submit_frame:
self.submit_frame.forget() self.submit_frame.forget()
@@ -293,10 +309,13 @@ class ScheduleJob(Frame):
if self.blender_cameras_frame: if self.blender_cameras_frame:
self.blender_cameras_frame.pack_forget() self.blender_cameras_frame.pack_forget()
show_cams_checkbutton = Checkbutton(pack_frame, text='Multiple Cameras', offvalue=False, onvalue=True, # multiple cameras checkbox
camera_count = len(scene_data.get('cameras', [])) if scene_data else 0
show_cams_checkbutton = Checkbutton(pack_frame, text=f'Multiple Cameras ({camera_count})', offvalue=False,
onvalue=True,
variable=self.blender_multiple_cameras, command=draw_scene_cams) variable=self.blender_multiple_cameras, command=draw_scene_cams)
show_cams_checkbutton.pack(side=LEFT, padx=5) show_cams_checkbutton.pack(side=LEFT, padx=5)
show_cams_checkbutton['state'] = NORMAL if scene_data and scene_data.get('cameras', None) else DISABLED show_cams_checkbutton['state'] = NORMAL if camera_count > 1 else DISABLED
def submit_job(self): def submit_job(self):
@@ -307,7 +326,7 @@ class ScheduleJob(Frame):
'renderer': renderer, 'renderer': renderer,
'client': client, 'client': client,
'output_path': os.path.join(os.path.dirname(self.chosen_file), self.output_entry.get()), 'output_path': os.path.join(os.path.dirname(self.chosen_file), self.output_entry.get()),
'args': {}, 'args': {'raw': self.custom_args_entry.get()},
'name': None} 'name': None}
job_list = [] job_list = []
@@ -332,8 +351,8 @@ class ScheduleJob(Frame):
job_json['args']['export_format'] = self.output_format.get() job_json['args']['export_format'] = self.output_format.get()
# multiple camera rendering # multiple camera rendering
selected_cameras = self.blender_cameras_list.getCheckedItems() if self.blender_cameras_list else None if self.blender_cameras_list and self.blender_multiple_cameras.get():
if selected_cameras: selected_cameras = self.blender_cameras_list.getCheckedItems()
for cam in selected_cameras: for cam in selected_cameras:
job_copy = copy.deepcopy(job_json) job_copy = copy.deepcopy(job_json)
job_copy['args']['camera'] = cam.split('-')[0].strip() job_copy['args']['camera'] = cam.split('-')[0].strip()