Please check out the first part:
https://www.enoni.de/wp/karma-on-deadline-via-husk/
Read to the lines where the scripts enhancements at the Deadline repository starts.
Lets take your favourite renderer to USD land and skip the part of making *.rs or *.orbx archives and render standalone via Husk.
Here is a small update on specific Python scripts to trigger specific render delegates. The tricky part is, that we have to pipe the ENV VARS through Deadline, so Husk will find specific locations to load the right renderer and versions – especially in case you uses custom starting procedures. Therefore i just read the HOUDINI_PACKAGE_DIR var and send this with the submission script at the current Houdini session. In case you dont use the Houdini package .json system you have the modify the scripts in other ways.
First step: edit the SubmitRenderJob_husk function :
*\DeadlineRepository10\submission\Houdini\Main\SubmitHoudiniToDeadlineFunctions.py
def SubmitRenderJob_husk( node, jobProperties, dependencies ):
if jobProperties.get("usdjob"):
if jobProperties.get("usdjob") == 1:
assemblyJobIds = []
jobName = jobProperties.get( "jobname", "Untitled" )
jobName = "%s - %s"%(jobName, node.path())
subInfo = json.loads( hou.getenv("Deadline_Submission_Info") )
homeDir = subInfo["UserHomeDir"]
jobInfoFile = os.path.join(homeDir, "temp", "houdini_submit_info.job")
## job file ##
with open( jobInfoFile, "w" ) as fileHandle:
fileHandle.write( "Plugin=HuskStandalone\n" )
fileHandle.write( "Name=%s\n" % jobName )
fileHandle.write( "Comment=%s\n" % jobProperties.get( "comment", "" ) )
fileHandle.write( "Department=%s\n" % jobProperties.get( "department", "" ) )
fileHandle.write( "Pool=%s\n" % jobProperties.get( "pool", "None" ) )
fileHandle.write( "SecondaryPool=%s\n" % jobProperties.get( "secondarypool", "" ) )
fileHandle.write( "Group=%s\n" % jobProperties.get( "group", "None" ) )
fileHandle.write( "Priority=%s\n" % jobProperties.get( "priority", 50 ) )
fileHandle.write( "TaskTimeoutMinutes=%s\n" % jobProperties.get( "tasktimeout", 0 ) )
fileHandle.write( "EnableAutoTimeout=%s\n" % jobProperties.get( "autotimeout", False ) )
fileHandle.write( "ConcurrentTasks=%s\n" % jobProperties.get( "concurrent", 1 ) )
fileHandle.write( "MachineLimit=%s\n" % jobProperties.get( "machinelimit", 0 ) )
fileHandle.write( "LimitConcurrentTasksToNumberOfCpus=%s\n" % jobProperties.get( "slavelimit", False ) )
fileHandle.write( "LimitGroups=%s\n" % jobProperties.get( "limits", 0 ) )
fileHandle.write( "JobDependencies=%s\n" % dependencies )
fileHandle.write( "OnJobComplete=%s\n" % jobProperties.get( "onjobcomplete", "Nothing" ) )
fileHandle.write( "Frames=%s\n" % GetFrameList( node, jobProperties) )
fileHandle.write( "ChunkSize=%s\n" % jobProperties.get( "framespertask", 1 ) )
pluginInfoFile = os.path.join( homeDir, "temp", "houdini_plugin_info.job")
with open( pluginInfoFile, "w" ) as fileHandle:
fileHandle.write( "SceneFile=%s\n" % hou.parm(node.path() + "/lopoutput").eval() )
fileHandle.write( "LogLevel=%s\n" % jobProperties.get( "usdloglevel", 2 ) )
fileHandle.write( "HouVersion=%s\n" % jobProperties.get( "ns_pipe_hou_version", "Nothing" ) )
fileHandle.write( "OutImage=%s\n" % jobProperties.get( "ns_pipe_image_out", "" ) )
fileHandle.write( "RenderDelegate=%s\n" % jobProperties.get( "ns_delegate", "" ) )
fileHandle.write( "HouPackageLocation=%s\n" % jobProperties.get( "ns_package", "" ) )
arguments = [ jobInfoFile, pluginInfoFile ]
jobResult = CallDeadlineCommand( arguments )
jobId = GetJobIdFromSubmission( jobResult )
assemblyJobIds.append( jobId )
print("---------------------------------------------------")
print("\n".join( [ line.strip() for line in jobResult.split("\n") if line.strip() ] ) )
print("---------------------------------------------------")
else:
return
else:
print("ns_Pipe> Found no usdjob property")
HouVersion, OutImage, RenderDelegate and HouPackageLocation are the new jobProperties i added. The values (ns_pipe_hou_version, ns_pipe_image_out, ns_delegate, ns_package) came from the Houdini submission script.
Next to edit: build the right arguments for Husk:
*\DeadlineRepository10\custom\plugins\HuskStandalone\HuskStandalone.py
from System import *
from System.Diagnostics import *
from System.IO import *
import os
from Deadline.Plugins import *
from Deadline.Scripting import *
from pathlib import Path
def GetDeadlinePlugin():
return HuskStandalone()
def CleanupDeadlinePlugin(deadlinePlugin):
deadlinePlugin.Cleanup()
class HuskStandalone(DeadlinePlugin):
# functions inside a class must be indented in python - DT
def __init__( self ):
self.InitializeProcessCallback += self.InitializeProcess
self.RenderExecutableCallback += self.RenderExecutable # get the renderExecutable Location
self.RenderArgumentCallback += self.RenderArgument # get the arguments to go after the EXE
def Cleanup( self ):
del self.InitializeProcessCallback
del self.RenderExecutableCallback
del self.RenderArgumentCallback
def InitializeProcess( self ):
self.SingleFramesOnly=True
self.StdoutHandling=True
self.PopupHandling=False
self.AddStdoutHandlerCallback("USD ERROR(.*)").HandleCallback += self.HandleStdoutError # detect this error
self.AddStdoutHandlerCallback( r"ALF_PROGRESS ([0-9]+(?=%))" ).HandleCallback += self.HandleStdoutProgress
# get path to the executable
def RenderExecutable(self):
return self.GetConfigEntry( "USD_RenderExecutable" ).replace("<ns_Pipe>", "Houdini " + self.GetPluginInfoEntry("HouVersion"))
# get the settings that go after the filename in the render command, 3Delight only has simple options.
def RenderArgument( self ):
if self.GetPluginInfoEntry("RenderDelegate") == "Redshift":
os.environ["HOUDINI_PACKAGE_DIR"] = self.GetPluginInfoEntry("HouPackageLocation") # set delegate location
# construct fileName
#this will only support 1 frame per task
usdFile = self.GetPluginInfoEntry("SceneFile")
usdFile = RepositoryUtils.CheckPathMapping( usdFile )
usdFile = usdFile.replace( "\\", "/" )
usdPaddingLength = FrameUtils.GetPaddingSizeFromFilename( usdFile )
frameNumber = self.GetStartFrame() # check this 2021 USD
argument = ""
argument += " --renderer HdRedshiftRendererPlugin"
argument += " --usd-input " + usdFile
argument += " --verbose a{}".format(self.GetPluginInfoEntry("LogLevel")) # alfred style output and full verbosity
argument += " --frame {}".format(frameNumber)
argument += " --frame-count 1" # only render 1 frame per task
## renderer handled in job file. ##
if self.GetPluginInfoEntry("OutImage") != "":
if not os.path.isdir(os.path.dirname(self.GetPluginInfoEntry("OutImage"))):
os.makedirs(os.path.dirname(self.GetPluginInfoEntry("OutImage")))
output_path = os.path.dirname(self.GetPluginInfoEntry("OutImage"))
out_image_path_parts = self.GetPluginInfoEntry("OutImage").split("/")
image_comp_parts = out_image_path_parts[-1].split(".")
image_name = image_comp_parts[0]
padded_frame_number = StringUtils.ToZeroPaddedString(frameNumber, len(image_comp_parts[-2]))
image_format = image_comp_parts[-1]
argument += " --output {0}/{1}.{2}.{3}".format(output_path, image_name, padded_frame_number, image_format)
else:
# fallback
outputPath = os.path.dirname(usdFile).split('/') #[:-4] We are now going to site the composite USD in the project root.
outputPath.append("render")
outputPath = os.path.abspath(os.path.join(*outputPath))
if not os.path.isdir(outputPath):
os.mkdir(outputPath)
filename = Path(usdFile).name
filename = Path(filename).with_suffix("")
paddedFrameNumber = StringUtils.ToZeroPaddedString(frameNumber, 4)
argument += " -o {0}/{1}.{2}.exr".format(outputPath, filename, paddedFrameNumber)
argument += " --make-output-path"
# argument += " --exrmode 0" ## legacy exr mode for fusion cryptomattes ##
# argument += " --ocio 1"
self.LogInfo( "Rendering USD file: " + usdFile )
elif self.GetPluginInfoEntry("RenderDelegate") == "Octane":
os.environ["HOUDINI_PACKAGE_DIR"] = self.GetPluginInfoEntry("HouPackageLocation") # set delegate location
# construct fileName
#this will only support 1 frame per task
usdFile = self.GetPluginInfoEntry("SceneFile")
usdFile = RepositoryUtils.CheckPathMapping( usdFile )
usdFile = usdFile.replace( "\\", "/" )
usdPaddingLength = FrameUtils.GetPaddingSizeFromFilename( usdFile )
frameNumber = self.GetStartFrame() # check this 2021 USD
argument = ""
argument += " --renderer HdOctaneRendererPlugin"
argument += " --usd-input " + usdFile
argument += " --verbose a{}".format(self.GetPluginInfoEntry("LogLevel")) # alfred style output and full verbosity
argument += " --frame {}".format(frameNumber)
argument += " --frame-count 1" # only render 1 frame per task
## renderer handled in job file. ##
if self.GetPluginInfoEntry("OutImage") != "":
if not os.path.isdir(os.path.dirname(self.GetPluginInfoEntry("OutImage"))):
os.makedirs(os.path.dirname(self.GetPluginInfoEntry("OutImage")))
output_path = os.path.dirname(self.GetPluginInfoEntry("OutImage"))
out_image_path_parts = self.GetPluginInfoEntry("OutImage").split("/")
image_comp_parts = out_image_path_parts[-1].split(".")
image_name = image_comp_parts[0]
padded_frame_number = StringUtils.ToZeroPaddedString(frameNumber, len(image_comp_parts[-2]))
image_format = image_comp_parts[-1]
argument += " --output {0}/{1}.{2}.{3}".format(output_path, image_name, padded_frame_number, image_format)
else:
# fallback
outputPath = os.path.dirname(usdFile).split('/') #[:-4] We are now going to site the composite USD in the project root.
outputPath.append("render")
outputPath = os.path.abspath(os.path.join(*outputPath))
if not os.path.isdir(outputPath):
os.mkdir(outputPath)
filename = Path(usdFile).name
filename = Path(filename).with_suffix("")
paddedFrameNumber = StringUtils.ToZeroPaddedString(frameNumber, 4)
argument += " -o {0}/{1}.{2}.exr".format(outputPath, filename, paddedFrameNumber)
argument += " --make-output-path"
# argument += " --exrmode 0" ## legacy exr mode for fusion cryptomattes ##
# argument += " --ocio 1"
self.LogInfo( "Rendering USD file: " + usdFile )
elif self.GetPluginInfoEntry("RenderDelegate") == "Karma":
# construct fileName
#this will only support 1 frame per task
usdFile = self.GetPluginInfoEntry("SceneFile")
usdFile = RepositoryUtils.CheckPathMapping( usdFile )
usdFile = usdFile.replace( "\\", "/" )
usdPaddingLength = FrameUtils.GetPaddingSizeFromFilename( usdFile )
frameNumber = self.GetStartFrame() # check this 2021 USD
argument = ""
argument += " --renderer BRAY_HdKarma"
argument += " --usd-input " + usdFile
argument += " --verbose a{}".format(self.GetPluginInfoEntry("LogLevel")) # alfred style output and full verbosity
argument += " --frame {}".format(frameNumber)
argument += " --frame-count 1" # only render 1 frame per task
## renderer handled in job file. ##
if self.GetPluginInfoEntry("OutImage") != "":
output_path = os.path.dirname(self.GetPluginInfoEntry("OutImage"))
out_image_path_parts = self.GetPluginInfoEntry("OutImage").split("/")
image_comp_parts = out_image_path_parts[-1].split(".")
image_name = image_comp_parts[0]
padded_frame_number = StringUtils.ToZeroPaddedString(frameNumber, len(image_comp_parts[-2]))
image_format = image_comp_parts[-1]
argument += " -o {0}/{1}.{2}.{3}".format(output_path, image_name, padded_frame_number, image_format)
else:
# fallback
outputPath = os.path.dirname(usdFile).split('/') #[:-4] We are now going to site the composite USD in the project root.
outputPath.append("render")
outputPath = os.path.abspath(os.path.join(*outputPath))
if not os.path.isdir(outputPath):
os.mkdir(outputPath)
filename = Path(usdFile).name
filename = Path(filename).with_suffix("")
paddedFrameNumber = StringUtils.ToZeroPaddedString(frameNumber, 4)
argument += " -o {0}/{1}.{2}.exr".format(outputPath, filename, paddedFrameNumber)
argument += " --make-output-path"
argument += " --exrmode 1" ## legacy exr mode for fusion cryptomattes ##
# argument += " --ocio 1"
self.LogInfo( "Rendering USD file: " + usdFile )
elif self.GetPluginInfoEntry("RenderDelegate") == "KarmaXPU":
# construct fileName
#this will only support 1 frame per task
usdFile = self.GetPluginInfoEntry("SceneFile")
usdFile = RepositoryUtils.CheckPathMapping( usdFile )
usdFile = usdFile.replace( "\\", "/" )
usdPaddingLength = FrameUtils.GetPaddingSizeFromFilename( usdFile )
frameNumber = self.GetStartFrame() # check this 2021 USD
argument = ""
argument += " --renderer BRAY_HdKarmaXPU"
argument += " --usd-input " + usdFile
argument += " --verbose a{}".format(self.GetPluginInfoEntry("LogLevel")) # alfred style output and full verbosity
argument += " --frame {}".format(frameNumber)
argument += " --frame-count 1" # only render 1 frame per task
## renderer handled in job file. ##
if self.GetPluginInfoEntry("OutImage") != "":
output_path = os.path.dirname(self.GetPluginInfoEntry("OutImage"))
out_image_path_parts = self.GetPluginInfoEntry("OutImage").split("/")
image_comp_parts = out_image_path_parts[-1].split(".")
image_name = image_comp_parts[0]
padded_frame_number = StringUtils.ToZeroPaddedString(frameNumber, len(image_comp_parts[-2]))
image_format = image_comp_parts[-1]
argument += " -o {0}/{1}.{2}.{3}".format(output_path, image_name, padded_frame_number, image_format)
else:
# fallback
outputPath = os.path.dirname(usdFile).split('/') #[:-4] We are now going to site the composite USD in the project root.
outputPath.append("render")
outputPath = os.path.abspath(os.path.join(*outputPath))
if not os.path.isdir(outputPath):
os.mkdir(outputPath)
filename = Path(usdFile).name
filename = Path(filename).with_suffix("")
paddedFrameNumber = StringUtils.ToZeroPaddedString(frameNumber, 4)
argument += " -o {0}/{1}.{2}.exr".format(outputPath, filename, paddedFrameNumber)
argument += " --make-output-path"
argument += " --exrmode 1" ## legacy exr mode for fusion cryptomattes ##
# argument += " --ocio 1"
self.LogInfo( "Rendering USD file: " + usdFile )
return argument
# just incase we want to implement progress at some point
def HandleStdoutProgress(self):
self.SetStatusMessage(self.GetRegexMatch(0))
self.SetProgress(float(self.GetRegexMatch(1)))
# what to do when an error is detected.
def HandleStdoutError(self):
self.FailRender(self.GetRegexMatch(0))
Here i added some code in the RenderExecutable and RenderArgument function. Just compare it with the original script.
You can see, for the Octane & Redshift delegate i set the ENV variable “HOUDINI_PACKAGE_DIR”, so Husk is loading the same version that i used in the setup file or Houdini session.
I added a new argument for Husk as well:
argument += " --exrmode 0"
This is for Fusion Cryptomattes .exr files. Otherwise the Cryptos wont working.
NOTE:
Check out other arguments to pass through, here: https://www.sidefx.com/docs/houdini/ref/utils/husk.html
For example to set a specific OCIO color transform with this line:
argument += " --ocio 1"
Create a custom Houdini Submitter Script for USD-ROPs.
You can just create a Houdini shelf tool or build an entry in the OPmenu.xml to trigger the script.
OPmenu.xml code:
<!--LOP LEVEL MENU-->
<!--############################################################################################################################-->
<expression>
node = kwargs.get("node", None)
if node is None:
return False
return node.type().category().name() in ["usdrender_rop", "usd_rop"]
</expression>
<subMenu id="ns_version_menu_lop">
<label>[ ns_Pipe ]</label>
<context>
</context>
<expression>
node = kwargs.get("node", None)
if node is None:
return False
return node.type().name() in ["usdrender_rop", "usd_rop"]
</expression>
<subMenu id="ns_version_deadline_menu_lop">
<label>Deadline</label>
<context>
</context>
<scriptItem id="ns_deadline_submitter_husk">
<expression>
node = kwargs.get("node", None)
if node is None:
return False
return node.type().name() in ["usdrender_rop", "usd_rop"]
</expression>
<label>Deadline Submitter Husk - Karma</label>
<scriptPath>$HSITE/scripts/python/ns_Pipe/ns_deadline_submitter_husk.py</scriptPath>
</scriptItem>
<scriptItem id="ns_deadline_submitter_husk_task">
<expression>
node = kwargs.get("node", None)
if node is None:
return False
return node.type().name() in ["usdrender_rop", "usd_rop"]
</expression>
<label>Deadline Submitter Husk per Task - Karma</label>
<scriptPath>$HSITE/scripts/python/ns_Pipe/ns_deadline_submitter_husk_task.py</scriptPath>
</scriptItem>
<separatorItem/>
<scriptItem id="ns_deadline_submitter_husk_XPU">
<expression>
node = kwargs.get("node", None)
if node is None:
return False
return node.type().name() in ["usdrender_rop", "usd_rop"]
</expression>
<label>Deadline Submitter Husk - KarmaXPU</label>
<scriptPath>$HSITE/scripts/python/ns_Pipe/ns_deadline_submitter_husk_xpu.py</scriptPath>
</scriptItem>
<scriptItem id="ns_deadline_submitter_husk_task_XPU">
<expression>
node = kwargs.get("node", None)
if node is None:
return False
return node.type().name() in ["usdrender_rop", "usd_rop"]
</expression>
<label>Deadline Submitter Husk per Task - KarmaXPU</label>
<scriptPath>$HSITE/scripts/python/ns_Pipe/ns_deadline_submitter_husk_task_xpu.py</scriptPath>
</scriptItem>
<separatorItem/>
<scriptItem id="ns_deadline_submitter_husk_rs">
<expression>
node = kwargs.get("node", None)
if node is None:
return False
return node.type().name() in ["usdrender_rop", "usd_rop"]
</expression>
<label>Deadline Submitter Husk - Redshift Delegate</label>
<scriptPath>$HSITE/scripts/python/ns_Pipe/ns_deadline_submitter_husk_rs.py</scriptPath>
</scriptItem>
<scriptItem id="ns_deadline_submitter_husk_task_rs">
<expression>
node = kwargs.get("node", None)
if node is None:
return False
return node.type().name() in ["usdrender_rop", "usd_rop"]
</expression>
<label>Deadline Submitter Husk per Task - Redshift Delegate</label>
<scriptPath>$HSITE/scripts/python/ns_Pipe/ns_deadline_submitter_husk_task_rs.py</scriptPath>
</scriptItem>
<separatorItem/>
<scriptItem id="ns_deadline_submitter_husk_octane">
<expression>
node = kwargs.get("node", None)
if node is None:
return False
return node.type().name() in ["usdrender_rop", "usd_rop"]
</expression>
<label>Deadline Submitter Husk - Octane Delegate</label>
<scriptPath>$HSITE/scripts/python/ns_Pipe/ns_deadline_submitter_husk_octane.py</scriptPath>
</scriptItem>
<scriptItem id="ns_deadline_submitter_husk_task_octane">
<expression>
node = kwargs.get("node", None)
if node is None:
return False
return node.type().name() in ["usdrender_rop", "usd_rop"]
</expression>
<label>Deadline Submitter Husk per Task - Octane Delegate</label>
<scriptPath>$HSITE/scripts/python/ns_Pipe/ns_deadline_submitter_husk_task_octane.py</scriptPath>
</scriptItem>
</subMenu>
This will create a script, that has an input prompt where you can define how many frames Deadline will render per task and which priority is needed. You can, of course, build a high advanced submitter with a lot more inputs, choices and a fancy GUI.
Here for Octane you have to create a “husk_octane” pool on Deadline:
## Niclas Schlapmann - Freelance 3D Technical Artist
## www.enoni.de
## hello@enoni.de
## 07.04.2024
##################################### Imports ####################################
import hou
import os
import sys
import traceback
import json
import getpass
import time
from time import *
##################################################################################
user = getpass.getuser()
lt = localtime()
year, month, day, hour, minute, sec = lt[0:6]
date = str(year)[2:4] + "-" + str(month).zfill(2) + "-" + str(day).zfill(2) + " - " + str(hour).zfill(2) + ":" + str(minute).zfill(2) + ":" + str(sec).zfill(2)
def deadline_submitter_husk_task_octane():
renderNodes = hou.selectedNodes()
if not renderNodes:
return
if renderNodes[0].type().name() not in ["usdrender_rop", "usd_rop"]:
hou.ui.displayMessage("Select a proper USD-ROP or USDRender-ROP")
return
for renderNode in renderNodes:
jobname = hou.getenv("HIPNAME")
pool = "husk_octane"
secondarypool = "husk_octane"
comment = "submitted by <" + user + "> " + date
department = "enoni.de"
if renderNode.evalParm(renderNode.path() + "/trange") >= 1:
framelist = str(int(renderNode.evalParm(renderNode.path() + "/f1"))) + "-" + str(int(renderNode.evalParm(renderNode.path() + "/f2")))
else:
framelist = str(int(hou.frame())) + "-" + str(int(hou.frame()))
framecount = int(renderNode.evalParm(renderNode.path() + "/f2")) - int(renderNode.evalParm(renderNode.path() + "/f1")) + 1
frame_input_count = hou.ui.readInput("Frames per task:", buttons=("OK", "Cancel"), initial_contents=str(framecount))
if frame_input_count[0] == 1:
return
print("ns_Pipe> Render ROP: " + renderNode.name())
choice = hou.ui.displayMessage("Deadline Priority?", buttons=("Extra Low(1)", "Low(25)", "Mid(50)", "High(99)", "Abort"))
if choice == 0:
prio = 1
elif choice == 1:
prio = 25
elif choice == 2:
prio = 50
elif choice == 3:
prio = 99
else:
return
## create prop dictionary ##
jobProperties = {
'batch': False,
'jobname': jobname,
'comment': comment,
'department': department,
'pool': pool,
'secondarypool': secondarypool,
'group': 'none',
'priority': prio,
'tasktimeout': 0,
'autotimeout': 0,
'concurrent': 1,
'machinelimit': 0,
'slavelimit': 1,
'limits': '',
'onjobcomplete': 'Nothing',
'jobsuspended': 0,
'shouldprecache': 1,
'isblacklist': 0,
'machinelist': '',
'overrideframes': 1,
'framelist': framelist,
'framespertask': int(frame_input_count[1]),
'bits': '64bit',
'submitscene': 0,
'isframedependent': 0,
'gpuopenclenable': 0,
'gpuspertask': 0,
'gpudevices': '',
'ignoreinputs': 0,
'separateWedgeJobs': 0,
'mantrajob': 0,
'mantrapool': pool,
'mantrasecondarypool': secondarypool,
'mantragroup': 'none',
'mantrapriority': prio,
'mantratasktimeout': 0,
'mantraautotimeout': 0,
'mantraconcurrent': 1,
'mantramachinelimit': 0,
'mantraslavelimit': 1,
'mantralimits': '',
'mantraonjobcomplete': 'Nothing',
'mantraisblacklist': 0,
'mantramachinelist': '',
'mantrathreads': 0,
'mantralocalexport': 0,
'arnoldjob': 1,
'arnoldpool': pool,
'arnoldsecondarypool': secondarypool,
'arnoldgroup': 'none',
'arnoldpriority': prio,
'arnoldtasktimeout': 0,
'arnoldautotimeout': 0,
'arnoldconcurrent': 1,
'arnoldmachinelimit': 0,
'arnoldslavelimit': 1,
'arnoldonjobcomplete': 'Nothing',
'arnoldlimits': '',
'arnoldisblacklist': 0,
'arnoldmachinelist': '',
'arnoldthreads': 0,
'arnoldlocalexport': 1,
'rendermanjob': 0,
'rendermanpool': pool,
'rendermansecondarypool': secondarypool,
'rendermangroup': 'none',
'rendermanpriority': prio,
'rendermantasktimeout': 0,
'rendermanconcurrent': 1,
'rendermanmachinelimit': 0,
'rendermanlimits': '',
'rendermanonjobcomplete': 'Nothing',
'rendermanisblacklist': 0,
'rendermanmachinelist': '',
'rendermanthreads': 0,
'rendermanarguments': '',
'rendermanlocalexport': 0,
'redshiftjob': 0,
'redshiftpool': pool,
'redshiftsecondarypool': secondarypool,
'redshiftgroup': 'none',
'redshiftpriority': prio,
'redshifttasktimeout': 0,
'redshiftautotimeout': 0,
'redshiftconcurrent': 1,
'redshiftmachinelimit': 0,
'redshiftslavelimit': 1,
'redshiftlimits': '',
'redshiftonjobcomplete': 'Nothing',
'redshiftisblacklist': 0,
'redshiftmachinelist': '',
'redshiftarguments': '',
'redshiftlocalexport': 0,
'usdjob': 1,
'usdpool': pool,
'usdsecondarypool': secondarypool,
'usdgroup': 'none',
'usdpriority': prio,
'usdtasktimeout': 0,
'usdautotimeout': 0,
'usdconcurrent': 1,
'usdmachinelimit': 0,
'usdslavelimit': 1,
'usdlimits': '',
'usdonjobcomplete': 'Nothing',
'usdisblacklist': 0,
'usdmachinelist': '',
'usdarguments': '',
'usdlocalexport': 1,
'usdloglevel': 2,
'vrayjob': 0,
'vraypool': pool,
'vraysecondarypool': secondarypool,
'vraygroup': 'none',
'vraypriority': prio,
'vraytasktimeout': 0,
'vrayautotimeout': 0,
'vrayconcurrent': 1,
'vraymachinelimit': 0,
'vrayslavelimit': 1,
'vraylimits': '',
'vrayonjobcomplete': 'Nothing',
'vrayisblacklist': 0,
'vraymachinelist': '',
'vraythreads': 0,
'vrayarguments': '',
'vraylocalexport': 0,
'tilesenabled': 0,
'tilesinx': 3,
'tilesiny': 3,
'tilessingleframeenabled': 1,
'tilessingleframe': 1,
'jigsawenabled': 1,
'jigsawregioncount': 0,
'jigsawregions': [],
'submitdependentassembly': 1,
'backgroundoption': 'Blank Image',
'backgroundimage': '',
'erroronmissingtiles': '1',
'erroronmissingbackground': '0',
'cleanuptiles': '1'
}
## write USD from USD-ROP ##
usd_file_path = renderNode.evalParm(renderNode.path() + "/lopoutput")
if os.path.isfile(usd_file_path):
if hou.ui.displayMessage("USD render file already exist. Override", buttons=("Yes", "Abort")) == 0:
renderNode.parm("execute").pressButton()
else:
return
else:
renderNode.parm("execute").pressButton()
tmp_version = ""
## submit to Deadline ##
flag = 0
## imports and sys pathes for deadline ##
try:
from CallDeadlineCommand import CallDeadlineCommand
except ImportError:
path = ""
print("The CallDeadlineCommand.py script could not be found in the Houdini installation. Please make sure that the Deadline Client has been installed on this machine.\n")
hou.ui.displayMessage("The CallDeadlineCommand.py script could not be found in the Houdini installation. Please make sure that the Deadline Client has been installed on this machine.", title="Submit Houdini To Deadline")
else:
path = CallDeadlineCommand(["-GetRepositoryPath", "submission/Houdini/Main"]).strip()
if path:
path = path.replace("\\", "/")
# Add the path to the system path
if path not in sys.path:
print("Appending \"" + path + "\" to system path to import SubmitHoudiniToDeadline module")
sys.path.append(path)
else:
pass
# Import the script and call the main() function
try:
import SubmitHoudiniToDeadline
except:
print(traceback.format_exc())
print("The SubmitHoudiniToDeadline.py script could not be found in the Deadline Repository. Please make sure that the Deadline Client has been installed on this machine, that the Deadline Client bin folder is set in the DEADLINE_PATH environment variable, and that the Deadline Client has been configured to point to a valid Repository.")
else:
print("The SubmitHoudiniToDeadline.py script could not be found in the Deadline Repository. Please make sure that the Deadline Client has been installed on this machine, that the Deadline Client bin folder is set in the DEADLINE_PATH environment variable, and that the Deadline Client has been configured to point to a valid Repository.")
## get deadline info ##
print("Grabbing submitter info...")
try:
output = json.loads(CallDeadlineCommand(["-prettyJSON", "-GetSubmissionInfo", "Pools", "Groups", "MaxPriority", "TaskLimit", "UserHomeDir", "RepoDir:submission/Houdini/Main", "RepoDir:submission/Integration/Main", "RepoDirNoCustom:draft",
"RepoDirNoCustom:submission/Jigsaw", ]))
except:
print("Unable to get submitter info from Deadline:\n\n" + traceback.format_exc())
raise
if output["ok"]:
submissionInfo = output["result"]
hou.putenv("Deadline_Submission_Info", json.dumps(submissionInfo))
else:
print("DeadlineCommand returned a bad result and was unable to grab the submitter info.\n\n" + output["result"])
raise Exception(output["result"])
## submit render job ##
try:
import SubmitHoudiniToDeadlineFunctions as SHTDFunctions
flag = 1
except Exception as e:
print(e)
hou.ui.displayMessage("ns_Pipe> Library import failure. Make sure you have a proper Deadline installation.")
if flag:
try:
jobProperties.update({"ns_delegate" : "Octane"})
jobProperties.update({"ns_package" : hou.getenv("HOUDINI_PACKAGE_DIR")})
jobProperties.update({"ns_pipe_hou_version": tmp_version})
jobProperties.update({"ns_pipe_image_out" : hou.parm(renderNode.path() + "/spare_output_image").eval()})
jobIds = SHTDFunctions.SubmitRenderJob_husk(renderNode, jobProperties, "")
except Exception as e:
print(e)
hou.ui.displayMessage("ns_Pipe> Can`t submitting to Deadline Repository.")
deadline_submitter_husk_task_octane()
Here for Octane you have to create a “husk_redshift” pool on Deadline:
## Niclas Schlapmann - Freelance 3D Technical Artist
## www.enoni.de
## hello@enoni.de
## 07.04.2024
##################################### Imports ####################################
import hou
import os
import sys
import traceback
import json
import getpass
import time
from time import *
##################################################################################
user = getpass.getuser()
lt = localtime()
year, month, day, hour, minute, sec = lt[0:6]
date = str(year)[2:4] + "-" + str(month).zfill(2) + "-" + str(day).zfill(2) + " - " + str(hour).zfill(2) + ":" + str(minute).zfill(2) + ":" + str(sec).zfill(2)
def deadline_submitter_husk_task_rs():
renderNodes = hou.selectedNodes()
if not renderNodes:
return
if renderNodes[0].type().name() not in ["usdrender_rop", "usd_rop"]:
hou.ui.displayMessage("Select a proper USD-ROP or USDRender-ROP")
return
for renderNode in renderNodes:
jobname = hou.getenv("HIPNAME")
pool = "husk_redshift"
secondarypool = "husk_redshift"
comment = "submitted by <" + user + "> " + date
department = "enoni.de"
if renderNode.evalParm(renderNode.path() + "/trange") >= 1:
framelist = str(int(renderNode.evalParm(renderNode.path() + "/f1"))) + "-" + str(int(renderNode.evalParm(renderNode.path() + "/f2")))
else:
framelist = str(int(hou.frame())) + "-" + str(int(hou.frame()))
framecount = int(renderNode.evalParm(renderNode.path() + "/f2")) - int(renderNode.evalParm(renderNode.path() + "/f1")) + 1
frame_input_count = hou.ui.readInput("Frames per task:", buttons=("OK", "Cancel"), initial_contents=str(framecount))
if frame_input_count[0] == 1:
return
print("ns_Pipe> Render ROP: " + renderNode.name())
choice = hou.ui.displayMessage("Deadline Priority?", buttons=("Extra Low(1)", "Low(25)", "Mid(50)", "High(99)", "Abort"))
if choice == 0:
prio = 1
elif choice == 1:
prio = 25
elif choice == 2:
prio = 50
elif choice == 3:
prio = 99
else:
return
## create prop dictionary ##
jobProperties = {
'batch': False,
'jobname': jobname,
'comment': comment,
'department': department,
'pool': pool,
'secondarypool': secondarypool,
'group': 'none',
'priority': prio,
'tasktimeout': 0,
'autotimeout': 0,
'concurrent': 1,
'machinelimit': 0,
'slavelimit': 1,
'limits': '',
'onjobcomplete': 'Nothing',
'jobsuspended': 0,
'shouldprecache': 1,
'isblacklist': 0,
'machinelist': '',
'overrideframes': 1,
'framelist': framelist,
'framespertask': int(frame_input_count[1]),
'bits': '64bit',
'submitscene': 0,
'isframedependent': 0,
'gpuopenclenable': 0,
'gpuspertask': 0,
'gpudevices': '',
'ignoreinputs': 0,
'separateWedgeJobs': 0,
'mantrajob': 0,
'mantrapool': pool,
'mantrasecondarypool': secondarypool,
'mantragroup': 'none',
'mantrapriority': prio,
'mantratasktimeout': 0,
'mantraautotimeout': 0,
'mantraconcurrent': 1,
'mantramachinelimit': 0,
'mantraslavelimit': 1,
'mantralimits': '',
'mantraonjobcomplete': 'Nothing',
'mantraisblacklist': 0,
'mantramachinelist': '',
'mantrathreads': 0,
'mantralocalexport': 0,
'arnoldjob': 1,
'arnoldpool': pool,
'arnoldsecondarypool': secondarypool,
'arnoldgroup': 'none',
'arnoldpriority': prio,
'arnoldtasktimeout': 0,
'arnoldautotimeout': 0,
'arnoldconcurrent': 1,
'arnoldmachinelimit': 0,
'arnoldslavelimit': 1,
'arnoldonjobcomplete': 'Nothing',
'arnoldlimits': '',
'arnoldisblacklist': 0,
'arnoldmachinelist': '',
'arnoldthreads': 0,
'arnoldlocalexport': 1,
'rendermanjob': 0,
'rendermanpool': pool,
'rendermansecondarypool': secondarypool,
'rendermangroup': 'none',
'rendermanpriority': prio,
'rendermantasktimeout': 0,
'rendermanconcurrent': 1,
'rendermanmachinelimit': 0,
'rendermanlimits': '',
'rendermanonjobcomplete': 'Nothing',
'rendermanisblacklist': 0,
'rendermanmachinelist': '',
'rendermanthreads': 0,
'rendermanarguments': '',
'rendermanlocalexport': 0,
'redshiftjob': 0,
'redshiftpool': pool,
'redshiftsecondarypool': secondarypool,
'redshiftgroup': 'none',
'redshiftpriority': prio,
'redshifttasktimeout': 0,
'redshiftautotimeout': 0,
'redshiftconcurrent': 1,
'redshiftmachinelimit': 0,
'redshiftslavelimit': 1,
'redshiftlimits': '',
'redshiftonjobcomplete': 'Nothing',
'redshiftisblacklist': 0,
'redshiftmachinelist': '',
'redshiftarguments': '',
'redshiftlocalexport': 0,
'usdjob': 1,
'usdpool': pool,
'usdsecondarypool': secondarypool,
'usdgroup': 'none',
'usdpriority': prio,
'usdtasktimeout': 0,
'usdautotimeout': 0,
'usdconcurrent': 1,
'usdmachinelimit': 0,
'usdslavelimit': 1,
'usdlimits': '',
'usdonjobcomplete': 'Nothing',
'usdisblacklist': 0,
'usdmachinelist': '',
'usdarguments': '',
'usdlocalexport': 1,
'usdloglevel': 2,
'vrayjob': 0,
'vraypool': pool,
'vraysecondarypool': secondarypool,
'vraygroup': 'none',
'vraypriority': prio,
'vraytasktimeout': 0,
'vrayautotimeout': 0,
'vrayconcurrent': 1,
'vraymachinelimit': 0,
'vrayslavelimit': 1,
'vraylimits': '',
'vrayonjobcomplete': 'Nothing',
'vrayisblacklist': 0,
'vraymachinelist': '',
'vraythreads': 0,
'vrayarguments': '',
'vraylocalexport': 0,
'tilesenabled': 0,
'tilesinx': 3,
'tilesiny': 3,
'tilessingleframeenabled': 1,
'tilessingleframe': 1,
'jigsawenabled': 1,
'jigsawregioncount': 0,
'jigsawregions': [],
'submitdependentassembly': 1,
'backgroundoption': 'Blank Image',
'backgroundimage': '',
'erroronmissingtiles': '1',
'erroronmissingbackground': '0',
'cleanuptiles': '1'
}
## write USD from USD-ROP ##
usd_file_path = renderNode.evalParm(renderNode.path() + "/lopoutput")
if os.path.isfile(usd_file_path):
if hou.ui.displayMessage("USD render file already exist. Override", buttons=("Yes", "Abort")) == 0:
renderNode.parm("execute").pressButton()
else:
return
else:
renderNode.parm("execute").pressButton()
tmp_version = ""
## submit to Deadline ##
flag = 0
## imports and sys pathes for deadline ##
try:
from CallDeadlineCommand import CallDeadlineCommand
except ImportError:
path = ""
print("The CallDeadlineCommand.py script could not be found in the Houdini installation. Please make sure that the Deadline Client has been installed on this machine.\n")
hou.ui.displayMessage("The CallDeadlineCommand.py script could not be found in the Houdini installation. Please make sure that the Deadline Client has been installed on this machine.", title="Submit Houdini To Deadline")
else:
path = CallDeadlineCommand(["-GetRepositoryPath", "submission/Houdini/Main"]).strip()
if path:
path = path.replace("\\", "/")
# Add the path to the system path
if path not in sys.path:
print("Appending \"" + path + "\" to system path to import SubmitHoudiniToDeadline module")
sys.path.append(path)
else:
pass
# Import the script and call the main() function
try:
import SubmitHoudiniToDeadline
except:
print(traceback.format_exc())
print("The SubmitHoudiniToDeadline.py script could not be found in the Deadline Repository. Please make sure that the Deadline Client has been installed on this machine, that the Deadline Client bin folder is set in the DEADLINE_PATH environment variable, and that the Deadline Client has been configured to point to a valid Repository.")
else:
print("The SubmitHoudiniToDeadline.py script could not be found in the Deadline Repository. Please make sure that the Deadline Client has been installed on this machine, that the Deadline Client bin folder is set in the DEADLINE_PATH environment variable, and that the Deadline Client has been configured to point to a valid Repository.")
## get deadline info ##
print("Grabbing submitter info...")
try:
output = json.loads(CallDeadlineCommand(["-prettyJSON", "-GetSubmissionInfo", "Pools", "Groups", "MaxPriority", "TaskLimit", "UserHomeDir", "RepoDir:submission/Houdini/Main", "RepoDir:submission/Integration/Main", "RepoDirNoCustom:draft",
"RepoDirNoCustom:submission/Jigsaw", ]))
except:
print("Unable to get submitter info from Deadline:\n\n" + traceback.format_exc())
raise
if output["ok"]:
submissionInfo = output["result"]
hou.putenv("Deadline_Submission_Info", json.dumps(submissionInfo))
else:
print("DeadlineCommand returned a bad result and was unable to grab the submitter info.\n\n" + output["result"])
raise Exception(output["result"])
## submit render job ##
try:
import SubmitHoudiniToDeadlineFunctions as SHTDFunctions
flag = 1
except Exception as e:
print(e)
hou.ui.displayMessage("ns_Pipe> Library import failure. Make sure you have a proper Deadline installation.")
if flag:
try:
jobProperties.update({"ns_delegate" : "Redshift"})
jobProperties.update({"ns_package" : hou.getenv("HOUDINI_PACKAGE_DIR")})
jobProperties.update({"ns_pipe_hou_version": tmp_version})
jobProperties.update({"ns_pipe_image_out" : hou.parm(renderNode.path() + "/spare_output_image").eval()})
jobIds = SHTDFunctions.SubmitRenderJob_husk(renderNode, jobProperties, "")
except Exception as e:
print(e)
hou.ui.displayMessage("ns_Pipe> Can`t submitting to Deadline Repository.")
deadline_submitter_husk_task_rs()
If you want to adding the Karma and KarmaX delegates as well, you have to change the “ns_delegate” value to “Karma” or “KarmaXPU”.
IMPORTANT:
You need on your USD-ROP a string field where the script find the path for the image output. The string var field has to be named “spare_output_image”. I just referencing the path from the Octane/Redshift Settings Node/LOP.
When you now trigger the script with selected USD-ROP node, you are able to send USD Husk render jobs to Deadline. Modify it to your specific needs and build your own Deadline submitter.