Merge pull request #211 from Colorbleed/REN-59

Improve render submission code
This commit is contained in:
Roy Nieterau 2018-12-08 19:42:52 +01:00 committed by GitHub
commit 934bbb280b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 221 additions and 129 deletions

View file

@ -2087,3 +2087,57 @@ def bake_to_world_space(nodes,
shape=shape)
return world_space_nodes
def get_attr_in_layer(attr, layer):
"""Return attribute value in specified renderlayer.
Same as cmds.getAttr but this gets the attribute's value in a
given render layer without having to switch to it.
Note: This is much faster for Maya's renderLayer system, yet the code
does no optimized query for render setup.
Args:
attr (str): attribute name, ex. "node.attribute"
layer (str): layer name
Returns:
The return value from `maya.cmds.getAttr`
"""
if cmds.mayaHasRenderSetup():
log.debug("lib.get_attr_in_layer is not optimized for render setup")
with renderlayer(layer):
return cmds.getAttr(attr)
connections = cmds.listConnections(attr,
plugs=True,
source=False,
destination=True,
type="renderLayer") or []
connections = filter(lambda x: x.endswith(".plug"), connections)
if not connections:
return cmds.getAttr(attr)
for connection in connections:
if connection.startswith(layer):
attr_split = connection.split(".")
if attr_split[0] == layer:
attr = ".".join(attr_split[0:-1])
return cmds.getAttr("%s.value" % attr)
else:
# When connections are present, but none
# to the specific renderlayer than the layer
# should have the "defaultRenderLayer"'s value
layer = "defaultRenderLayer"
for connection in connections:
if connection.startswith(layer):
attr_split = connection.split(".")
if attr_split[0] == "defaultRenderLayer":
attr = ".".join(attr_split[0:-1])
return cmds.getAttr("%s.value" % attr)
return cmds.getAttr(attr)

View file

@ -32,3 +32,38 @@ class Extractor(pyblish.api.InstancePlugin):
instance.data['stagingDir'] = staging_dir
return staging_dir
def contextplugin_should_run(plugin, context):
"""Return whether the ContextPlugin should run on the given context.
This is a helper function to work around a bug pyblish-base#250
Whenever a ContextPlugin sets specific families it will still trigger even
when no instances are present that have those families.
This actually checks it correctly and returns whether it should run.
"""
required = set(plugin.families)
# When no filter always run
if "*" in required:
return True
for instance in context:
# Ignore inactive instances
if (not instance.data.get("publish", True) or
not instance.data.get("active", True)):
continue
families = instance.data.get("families", [])
if any(f in required for f in families):
return True
family = instance.data.get("family")
if family and family in required:
return True
return False

View file

@ -2,6 +2,7 @@ import os
import subprocess
import pyblish.api
from colorbleed.plugin import contextplugin_should_run
CREATE_NO_WINDOW = 0x08000000
@ -40,6 +41,10 @@ class CollectDeadlineUser(pyblish.api.ContextPlugin):
def process(self, context):
"""Inject the current working file"""
# Workaround bug pyblish-base#250
if not contextplugin_should_run(self, context):
return
user = deadline_command("GetCurrentUserName").strip()
if not user:

View file

@ -11,4 +11,4 @@ class CollectMachineName(pyblish.api.ContextPlugin):
machine_name = socket.gethostname()
self.log.info("Machine name: %s" % machine_name)
context.data.update({"machine": machine_name})
context.data["machine"] = machine_name

View file

@ -147,11 +147,14 @@ class SubmitDependentImageSequenceJobDeadline(pyblish.api.InstancePlugin):
subset=subset
)
# Add in start/end frame
# Get start/end frame from instance, if not available get from context
context = instance.context
start = instance.data.get("startFrame", context.data["startFrame"])
end = instance.data.get("endFrame", context.data["endFrame"])
resources = []
start = instance.data.get("startFrame")
if start is None:
start = context.data["startFrame"]
end = instance.data.get("endFrame")
if end is None:
end = context.data["endFrame"]
# Add in regex for sequence filename
# This assumes the output files start with subset name and ends with
@ -187,6 +190,7 @@ class SubmitDependentImageSequenceJobDeadline(pyblish.api.InstancePlugin):
if not os.path.isdir(output_dir):
os.makedirs(output_dir)
resources = []
if data.get("extendFrames", False):
family = "colorbleed.imagesequence"

View file

@ -6,10 +6,9 @@ import colorbleed.maya.lib as lib
class CollectRenderLayerAOVS(pyblish.api.InstancePlugin):
"""Validate all render layer's AOVs / Render Elements are registered in
the database
"""Collect all render layer's AOVs / Render Elements that will render.
This validator is important to be able to Extend Frames
This collector is important to be able to Extend Frames.
Technical information:
Each renderer uses different logic to work with render passes.
@ -37,8 +36,7 @@ class CollectRenderLayerAOVS(pyblish.api.InstancePlugin):
return
# Get renderer
renderer = cmds.getAttr("defaultRenderGlobals.currentRenderer")
renderer = instance.data["renderer"]
self.log.info("Renderer found: {}".format(renderer))
rp_node_types = {"vray": ["VRayRenderElement", "VRayRenderElementSet"],
@ -53,21 +51,20 @@ class CollectRenderLayerAOVS(pyblish.api.InstancePlugin):
# Collect all AOVs / Render Elements
layer = instance.data["setMembers"]
with lib.renderlayer(layer):
node_type = rp_node_types[renderer]
render_elements = cmds.ls(type=node_type)
node_type = rp_node_types[renderer]
render_elements = cmds.ls(type=node_type)
# Check if AOVs / Render Elements are enabled
for element in render_elements:
enabled = lib.get_attr_in_layer("{}.enabled".format(element),
layer=layer)
if not enabled:
continue
# Check if AOVs / Render Elements are enabled
for element in render_elements:
enabled = cmds.getAttr("{}.enabled".format(element))
if not enabled:
continue
pass_name = self.get_pass_name(renderer, element)
render_pass = "%s.%s" % (instance.data["subset"], pass_name)
pass_name = self.get_pass_name(renderer, element)
render_pass = "%s.%s" % (instance.data["subset"], pass_name)
result.append(render_pass)
result.append(render_pass)
self.log.info("Found {} render elements / AOVs for "
"'{}'".format(len(result), instance.data["subset"]))

View file

@ -18,10 +18,9 @@ class CollectRenderableCamera(pyblish.api.InstancePlugin):
layer = instance.data["setMembers"]
cameras = cmds.ls(type="camera", long=True)
with lib.renderlayer(layer):
renderable = [c for c in cameras if
cmds.getAttr("%s.renderable" % c)]
renderable = [c for c in cameras if
lib.get_attr_in_layer("%s.renderable" % c, layer=layer)]
self.log.info("Found cameras %s" % len(renderable))
self.log.info("Found cameras %s: %s" % (len(renderable), renderable))
instance.data.update({"cameras": renderable})
instance.data["cameras"] = renderable

View file

@ -22,16 +22,10 @@ class CollectMayaRenderlayers(pyblish.api.ContextPlugin):
try:
render_globals = cmds.ls("renderglobalsDefault")[0]
except IndexError:
self.log.error("Cannot collect renderlayers without "
"renderGlobals node")
self.log.info("Skipping renderlayer collection, no "
"renderGlobalsDefault found..")
return
# Get start and end frame
start_frame = self.get_render_attribute("startFrame")
end_frame = self.get_render_attribute("endFrame")
context.data["startFrame"] = start_frame
context.data["endFrame"] = end_frame
# Get all valid renderlayers
# This is how Maya populates the renderlayer display
rlm_attribute = "renderLayerManager.renderLayerId"
@ -59,30 +53,34 @@ class CollectMayaRenderlayers(pyblish.api.ContextPlugin):
if layer.endswith("defaultRenderLayer"):
layername = "masterLayer"
else:
# Remove Maya render setup prefix `rs_`
layername = layer.split("rs_", 1)[-1]
# Get layer specific settings, might be overrides
with lib.renderlayer(layer):
data = {
"subset": layername,
"setMembers": layer,
"publish": True,
"startFrame": self.get_render_attribute("startFrame"),
"endFrame": self.get_render_attribute("endFrame"),
"byFrameStep": self.get_render_attribute("byFrameStep"),
"renderer": self.get_render_attribute("currentRenderer"),
data = {
"subset": layername,
"setMembers": layer,
"publish": True,
"startFrame": self.get_render_attribute("startFrame",
layer=layer),
"endFrame": self.get_render_attribute("endFrame",
layer=layer),
"byFrameStep": self.get_render_attribute("byFrameStep",
layer=layer),
"renderer": self.get_render_attribute("currentRenderer",
layer=layer),
# instance subset
"family": "Render Layers",
"families": ["colorbleed.renderlayer"],
"asset": asset,
"time": api.time(),
"author": context.data["user"],
# instance subset
"family": "Render Layers",
"families": ["colorbleed.renderlayer"],
"asset": asset,
"time": api.time(),
"author": context.data["user"],
# Add source to allow tracing back to the scene from
# which was submitted originally
"source": filepath
}
# Add source to allow tracing back to the scene from
# which was submitted originally
"source": filepath
}
# Apply each user defined attribute as data
for attr in cmds.listAttr(layer, userDefined=True) or list():
@ -112,8 +110,9 @@ class CollectMayaRenderlayers(pyblish.api.ContextPlugin):
instance.data["label"] = label
instance.data.update(data)
def get_render_attribute(self, attr):
return cmds.getAttr("defaultRenderGlobals.{}".format(attr))
def get_render_attribute(self, attr, layer):
return lib.get_attr_in_layer("defaultRenderGlobals.{}".format(attr),
layer=layer)
def parse_options(self, render_globals):
"""Get all overrides with a value, skip those without

View file

@ -29,7 +29,8 @@ class CollectVRayScene(pyblish.api.ContextPlugin):
# Get VRay Scene instance
vray_scenes = host.lsattr("family", "colorbleed.vrayscene")
if not vray_scenes:
self.log.info("No instance found of family: `colorbleed.vrayscene`")
self.log.info("Skipping vrayScene collection, no "
"colorbleed.vrayscene instance found..")
return
assert len(vray_scenes) == 1, "Multiple vrayscene instances found!"
@ -51,13 +52,6 @@ class CollectVRayScene(pyblish.api.ContextPlugin):
vrscene = ("vrayscene", "<Scene>", "<Scene>_<Layer>", "<Layer>")
vrscene_output = os.path.join(work_dir, *vrscene)
vrscene_data["startFrame"] = start_frame
vrscene_data["endFrame"] = end_frame
vrscene_data["vrsceneOutput"] = vrscene_output
context.data["startFrame"] = start_frame
context.data["endFrame"] = end_frame
# Check and create render output template for render job
# outputDir is required for submit_publish_job
if not vrscene_data.get("suspendRenderJob", False):
@ -103,7 +97,10 @@ class CollectVRayScene(pyblish.api.ContextPlugin):
# Add source to allow tracing back to the scene from
# which was submitted originally
"source": file_name
"source": file_name,
# Store VRay Scene additional data
"vrsceneOutput": vrscene_output
}
data.update(vrscene_data)

View file

@ -41,7 +41,7 @@ class VraySubmitDeadline(pyblish.api.InstancePlugin):
filename = os.path.basename(filepath)
task_name = "{} - {}".format(filename, instance.name)
batch_name = "VRay Scene Export - {}".format(filename)
batch_name = "{} - (vrscene)".format(filename)
# Get the output template for vrscenes
vrscene_output = instance.data["vrsceneOutput"]
@ -63,7 +63,9 @@ class VraySubmitDeadline(pyblish.api.InstancePlugin):
"BatchName": batch_name,
# Job name, as seen in Monitor
"Name": "{} [{}-{}]".format(task_name, start_frame, end_frame),
"Name": "Export {} [{}-{}]".format(task_name,
start_frame,
end_frame),
# Arbitrary username, for visualisation in Monitor
"UserName": deadline_user,

View file

@ -1,6 +1,7 @@
import pyblish.api
from maya import cmds
from colorbleed.plugin import contextplugin_should_run
class ValidateCurrentRenderLayerIsRenderable(pyblish.api.ContextPlugin):
@ -20,7 +21,12 @@ class ValidateCurrentRenderLayerIsRenderable(pyblish.api.ContextPlugin):
hosts = ["maya"]
families = ["colorbleed.renderlayer"]
def process(self, instance):
def process(self, context):
# Workaround bug pyblish-base#250
if not contextplugin_should_run(self, context):
return
layer = cmds.editRenderLayerGlobals(query=True, currentRenderLayer=True)
cameras = cmds.ls(type="camera", long=True)
renderable = any(c for c in cameras if cmds.getAttr(c + ".renderable"))

View file

@ -2,6 +2,7 @@ import pyblish.api
import avalon.api as api
from avalon.vendor import requests
from colorbleed.plugin import contextplugin_should_run
class ValidateDeadlineConnection(pyblish.api.ContextPlugin):
@ -12,7 +13,11 @@ class ValidateDeadlineConnection(pyblish.api.ContextPlugin):
hosts = ["maya"]
families = ["colorbleed.renderlayer"]
def process(self, instance):
def process(self, context):
# Workaround bug pyblish-base#250
if not contextplugin_should_run(self, context):
return
AVALON_DEADLINE = api.Session.get("AVALON_DEADLINE",
"http://localhost:8082")
@ -24,4 +29,4 @@ class ValidateDeadlineConnection(pyblish.api.ContextPlugin):
assert response.ok, "Response must be ok"
assert response.text.startswith("Deadline Web Service "), (
"Web service did not respond with 'Deadline Web Service'"
)
)

View file

@ -3,7 +3,6 @@ from maya import cmds
import pyblish.api
import colorbleed.api
import colorbleed.maya.action
import colorbleed.maya.lib as lib
class ValidateRenderNoDefaultCameras(pyblish.api.InstancePlugin):
@ -18,20 +17,14 @@ class ValidateRenderNoDefaultCameras(pyblish.api.InstancePlugin):
@staticmethod
def get_invalid(instance):
layer = instance.data["setMembers"]
renderable = set(instance.data["cameras"])
# Collect default cameras
cameras = cmds.ls(type='camera', long=True)
defaults = [cam for cam in cameras if
cmds.camera(cam, query=True, startupCamera=True)]
defaults = set(cam for cam in cameras if
cmds.camera(cam, query=True, startupCamera=True))
invalid = []
with lib.renderlayer(layer):
for cam in defaults:
if cmds.getAttr(cam + ".renderable"):
invalid.append(cam)
return invalid
return [cam for cam in renderable if cam in defaults]
def process(self, instance):
"""Process all the cameras in the instance"""

View file

@ -17,17 +17,28 @@ class ValidateRenderSingleCamera(pyblish.api.InstancePlugin):
order = colorbleed.api.ValidateContentsOrder
label = "Render Single Camera"
hosts = ['maya']
families = ['colorbleed.renderlayer',
families = ["colorbleed.renderlayer",
"colorbleed.vrayscene"]
actions = [colorbleed.maya.action.SelectInvalidAction]
def process(self, instance):
"""Process all the cameras in the instance"""
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("Invalid cameras for render.")
@classmethod
def get_invalid(cls, instance):
cameras = instance.data.get("cameras", [])
if len(cameras) != 1:
cls.log.error("Multiple renderable cameras" "found: %s " %
instance.data["setMembers"])
cameras = instance.data.get("cameras", [])
if len(cameras) > 1:
cls.log.error("Multiple renderable cameras found for %s: %s " %
(instance.data["setMembers"], cameras))
return [instance.data["setMembers"]] + cameras
elif len(cameras) < 1:
cls.log.error("No renderable cameras found for %s " %
instance.data["setMembers"])
return [instance.data["setMembers"]]

View file

@ -50,37 +50,33 @@ class ValidateRenderSettings(pyblish.api.InstancePlugin):
invalid = False
renderer = instance.data['renderer']
layer_node = instance.data['setMembers']
layer = instance.data['setMembers']
# Collect the filename prefix in the render layer
with lib.renderlayer(layer_node):
# Get the node attributes for current renderer
attrs = lib.RENDER_ATTRS.get(renderer, lib.RENDER_ATTRS['default'])
prefix = lib.get_attr_in_layer("{node}.{prefix}".format(**attrs),
layer=layer)
padding = lib.get_attr_in_layer("{node}.{padding}".format(**attrs),
layer=layer)
render_attrs = lib.RENDER_ATTRS.get(renderer,
lib.RENDER_ATTRS['default'])
node = render_attrs["node"]
padding_attr = render_attrs["padding"]
prefix_attr = render_attrs["prefix"]
anim_override = lib.get_attr_in_layer("defaultRenderGlobals.animation",
layer=layer)
if not anim_override:
invalid = True
cls.log.error("Animation needs to be enabled. Use the same "
"frame for start and end to render single frame")
prefix = cmds.getAttr("{}.{}".format(node, prefix_attr))
padding = cmds.getAttr("{}.{}".format(node, padding_attr))
fname_prefix = cls.RENDERER_PREFIX.get(renderer,
cls.DEFAULT_PREFIX)
if prefix != fname_prefix:
invalid = True
cls.log.error("Wrong file name prefix: %s (expected: %s)"
% (prefix, fname_prefix))
anim_override = cmds.getAttr("defaultRenderGlobals.animation")
if not anim_override:
invalid = True
cls.log.error("Animation needs to be enabled. Use the same "
"frame for start and end to render single frame")
fname_prefix = cls.RENDERER_PREFIX.get(renderer,
cls.DEFAULT_PREFIX)
if prefix != fname_prefix:
invalid = True
cls.log.error("Wrong file name prefix, expecting %s"
% fname_prefix)
if padding != cls.DEFAULT_PADDING:
invalid = True
cls.log.error("Expecting padding of {} ( {} )".format(
cls.DEFAULT_PADDING, "0" * cls.DEFAULT_PADDING))
if padding != cls.DEFAULT_PADDING:
invalid = True
cls.log.error("Expecting padding of {} ( {} )".format(
cls.DEFAULT_PADDING, "0" * cls.DEFAULT_PADDING))
return invalid

View file

@ -30,7 +30,6 @@ class ValidateSceneSetWorkspace(pyblish.api.ContextPlugin):
order = colorbleed.api.ValidatePipelineOrder
hosts = ['maya']
families = ['colorbleed.model']
category = 'scene'
version = (0, 1, 0)
label = 'Maya Workspace Set'

View file

@ -1,5 +1,6 @@
import pyblish.api
import colorbleed.api
from colorbleed.plugin import contextplugin_should_run
from maya import cmds
@ -13,6 +14,10 @@ class ValidateVRayTranslatorEnabled(pyblish.api.ContextPlugin):
def process(self, context):
# Workaround bug pyblish-base#250
if not contextplugin_should_run(self, context):
return
invalid = self.get_invalid(context)
if invalid:
raise RuntimeError("Found invalid VRay Translator settings!")
@ -22,21 +27,6 @@ class ValidateVRayTranslatorEnabled(pyblish.api.ContextPlugin):
invalid = False
# Check if there are any vray scene instances
# The reason to not use host.lsattr() as used in collect_vray_scene
# is because that information is already available in the context
vrayscene_instances = [i for i in context[:] if i.data["family"]
in cls.families]
if not vrayscene_instances:
cls.log.info("No VRay Scene instances found, skipping..")
return
# Ignore if no VRayScenes are enabled for publishing
if not any(i.data.get("publish", True) for i in vrayscene_instances):
cls.log.info("VRay Scene instances are disabled, skipping..")
return
# Get vraySettings node
vray_settings = cmds.ls(type="VRaySettingsNode")
assert vray_settings, "Please ensure a VRay Settings Node is present"