Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GafferDispatch.FrameMask node #2558

Merged
merged 4 commits into from May 1, 2018
Merged

Conversation

johnhaddon
Copy link
Member

Needed by Alex, so I thought I'd squeeze it in quickly to finish off the day.

def __init__( self, name = "FrameMask" ) :

GafferDispatch.TaskNode.__init__( self, name )
self["mask"] = Gaffer.StringPlug( defaultValue = "1-100x10" )
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems like an odd default to me.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's actually the same as the Dispatcher.frameRange plug, which I think was chosen on the grounds that it documents the syntax somewhat. I guess it does seem stranger here because it's a primary plug rather than being hidden until "Custom Range" is chosen like on the dispatcher. Would you prefer just ""? If so, does that pass through all frames or mask them all? I think I'd be inclined to have it as a pass-through.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"" and pass-through makes sense to me

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in d740e8c - thanks for the suggestion.

import Gaffer
import GafferDispatch

class FrameMask( GafferDispatch.TaskNode ) :
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be TaskFrameMask to match TaskContextVariables? I could see a future where we have SceneFrameMask and ImageFrameMask as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it really hard to imagine SceneFrameMask and ImageFrameMask for some reason, and I'm not aware of anyone every having asked for it. Perhaps this is because in many cases the "enabled" plug can be used for a similar purpose?

I suggest we stick with the simpler FrameMask for now, knowing that we can easily use a compatibility config if we need to in the future.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it really hard to imagine SceneFrameMask and ImageFrameMask for some reason, and I'm not aware of anyone every having asked for it.

See the IE published Box named "frameHold" which is essentially an ImageFrameMask. I coudn't find a published version for scenes, but I've definitely sent people the "SceneContextVariables with a variable called 'frame' and an expression driving the value" example many times.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Network issues are preventing me checking those out, but holding a frame and masking it are different things aren't they? Holding means the image/scene still exists, but is taking it's data from a different upstream frame. Masking means the task no longer exists at all. I'm going to stick to my guns on this one, with the fallback that I'm perfectly happy to rename the node if the need arises in the future...

I've definitely sent people the "SceneContextVariables with a variable called 'frame' and an expression driving the value" example many times.

Any reason you're not sending them a SceneTimeWarp?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

holding a frame and masking it are different things aren't they? Holding means the image/scene still exists, but is taking it's data from a different upstream frame.

Yes, but inverting your expression turns the mask into a hold, right? I guess its better described as a frame clamp? The use case here is to dispatch an enormous range (0-3000) and use those boxes to make sure certain branches don't render outside a start/stop frame range. So they're defining the frames to keep rather than the frames to skip.

Any reason you're not sending them a SceneTimeWarp?

I guess because its easier to tell someone "make an expression that drives the frame however you want" than to do the same for speed and offset.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but inverting your expression turns the mask into a hold, right? I guess its better described as a frame clamp?

Sorry, I'm not following, maybe just because I've been unable to see the frameHold example. To me, "hold" means a particular upstream frame is substituted in for some downstream frames. For instance, all frames greater than 10 might read frame 10 instead, so frame 10 is repeated. And "mask" means to ignore the tasks for some frames entirely. If you invert the mask, you're just ignoring a different set of frames, not holding anything. Nothing is repeated.

The use case here is to dispatch an enormous range (0-3000) and use those boxes to make sure certain branches don't render outside a start/stop frame range. So they're defining the frames to keep rather than the frames to skip.

Yeah, that's why the FrameMask node accepts the list of frames to keep.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm mixing things up... lets just end this discussion, its gotten maddening.

Any frames not included here will be ignored, regardless
of the dispatcher's frame range.

> Note : This can only remove frames. To add frames, edit the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid note, but perhaps a sign that the node is a bit more limiting than it could be. Is there a case for TaskTimeWarp or something along those lines?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A TaskTimeWarp wouldn't add frames either - it would offset or multiply them. It's not impossible to add frames, but you'd have to declare them as preTasks of something, which would make for pretty odd looking job structures in something like Tractor. A FrameMask is a simple concept intended for a specific purpose that has come up multiple times, and I don't think additional features are needed.

If we truly do want to add frames (and that's not what was asked for here), then I think the way to do it is to have some sort of auto-discovery mechanism whereby the Dispatcher gets the valid frame ranges automatically by examining the TaskNodes. But even then, what would adding frames achieve? If an upstream task says it is only valid for a particular range, then logically we can only mask from that range anyway, not add to it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A TaskTimeWarp wouldn't add frames either - it would offset or multiply them.

Good point. Still, offsetting the range might be appropriate in same cases, like a frame 1-100 geometry (or vdb) cache, followed by several renders in the real shot range.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely. I'm not trying to say that TaskTimeWarp isn't a valid concept. I'm just saying that it's a different concept to a FrameMask, and would be implemented as a different node.

def preTasks( self, context ) :

frames = IECore.FrameList.parse( self["mask"].getValue() ).asList()
if context.getFrame() in frames :
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the behaviour here is that an empty mask acts as a blocker. Should empty be equivalent of pass-through (eg disabled) instead? I'm not sure which is better to be honest.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I think I'm leaning to making the default value be "", and make this be a pass-through. Agreed?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, pass-through sounds good

@andrewkaufman
Copy link
Contributor

If you fixup the failing test we can merge. Feel free to squash first.

@johnhaddon
Copy link
Member Author

If you fixup the failing test we can merge. Feel free to squash first.

Fingers crossed, should be good to go now. Thanks for the review - the default behaviour is a definite improvement over what I first had.

@andrewkaufman andrewkaufman merged commit 81f5253 into GafferHQ:master May 1, 2018
@johnhaddon johnhaddon deleted the frameMask branch May 16, 2018 14:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants