Gaffer::Node *DotNodeGadget::upstreamNode() { Plug *plug = dotNode()->inPlug<Plug>(); while( plug && runTimeCast<Dot>( plug->node() ) ) { plug = plug->getInput<Plug>(); } return plug ? plug->node() : NULL; }
void BoxIO::insert( Box *box ) { // Must take a copy of children because adding a child // would invalidate our PlugIterator. GraphComponent::ChildContainer children = box->children(); for( PlugIterator it( children ); !it.done(); ++it ) { Plug *plug = it->get(); if( plug->direction() == Plug::In ) { std::vector<Plug *> outputsNeedingBoxIn; const Plug::OutputContainer &outputs = plug->outputs(); for( Plug::OutputContainer::const_iterator oIt = outputs.begin(), oeIt = outputs.end(); oIt != oeIt; ++oIt ) { if( hasNodule( *oIt ) && !runTimeCast<BoxIO>( (*oIt)->node() ) ) { outputsNeedingBoxIn.push_back( *oIt ); } } if( outputsNeedingBoxIn.empty() ) { continue; } BoxInPtr boxIn = new BoxIn; boxIn->namePlug()->setValue( plug->getName() ); boxIn->setup( plug ); box->addChild( boxIn ); boxIn->inPlugInternal()->setInput( plug ); for( std::vector<Plug *>::const_iterator oIt = outputsNeedingBoxIn.begin(), oeIt = outputsNeedingBoxIn.end(); oIt != oeIt; ++oIt ) { (*oIt)->setInput( boxIn->plug() ); } } else { // Output plug Plug *input = plug->getInput(); if( !input || !hasNodule( input ) || runTimeCast<BoxIO>( input->node() ) ) { continue; } BoxOutPtr boxOut = new BoxOut; boxOut->namePlug()->setValue( plug->getName() ); boxOut->setup( plug ); box->addChild( boxOut ); boxOut->plug()->setInput( input ); plug->setInput( boxOut->outPlugInternal() ); } } }
void TaskNode::postTasks( const Context *context, Tasks &tasks ) const { for( PlugIterator cIt( postTasksPlug() ); !cIt.done(); ++cIt ) { Plug *source = (*cIt)->source(); if( source != *cIt && source->direction() == Plug::Out ) { if( TaskNodePtr n = runTimeCast<TaskNode>( source->node() ) ) { tasks.push_back( Task( n, context ) ); } } } }
void TaskNode::preTasks( const Context *context, Tasks &tasks ) const { for( PlugIterator cIt( preTasksPlug() ); !cIt.done(); ++cIt ) { Plug *source = (*cIt)->source<Plug>(); if( source != *cIt ) { if( TaskNodePtr n = runTimeCast<TaskNode>( source->node() ) ) { tasks.push_back( Task( n, context ) ); } } } }
void ExecutableNode::requirements( const Context *context, Tasks &requirements ) const { for( PlugIterator cIt( requirementsPlug() ); cIt != cIt.end(); ++cIt ) { Plug *p = (*cIt)->source<Plug>(); if( p != *cIt ) { if( ExecutableNode *n = runTimeCast<ExecutableNode>( p->node() ) ) { /// \todo Can we not just reuse the context? Maybe we need to make /// the context in Task const? requirements.push_back( Task( n, new Context( *context ) ) ); } } } }
void emit() { // Because we hold a reference to the plugs via m_graph, // we may be the last owner. This means that when we clear // the graph below, those plugs may be destroyed, which can // trigger another dirty propagation as their child plugs are // removed etc. // // Additionally, emitting plugDirtiedSignal() can cause // ill-behaved code to trigger another dirty propagation // phase while we're emitting this one. This is explicitly // disallowed in the documentation for the Node class, but // unfortunately we can't control what the python interpreter // does - entering python via plugDirtiedSignal() can // trigger a garbage collection which might delete plugs // and trigger dirty propagation again as their children // and inputs are removed. // // We use the m_emitting flag to disable these unwanted // secondary propagations during emit(), since they're not // needed, and can cause crashes. ScopedAssignment<bool> scopedAssignment( m_emitting, true ); std::vector<VertexDescriptor> sorted; try { topological_sort( m_graph, std::back_inserter( sorted ) ); } catch( const std::exception &e ) { IECore::msg( IECore::Msg::Error, "Plug dirty propagation", e.what() ); } for( std::vector<VertexDescriptor>::const_iterator it = sorted.begin(), eIt = sorted.end(); it != eIt; ++it ) { Plug *plug = m_graph[*it].get(); plug->dirty(); if( Node *node = plug->node() ) { node->plugDirtiedSignal()( plug ); } } m_graph.clear(); m_plugs.clear(); }
static NodePtr node( Plug &p ) { return p.node(); }
void PathFilter::plugDirtied( const Gaffer::Plug *plug ) { if( plug == pathsPlug() ) { //\todo: share this logic with Switch::variesWithContext() Plug* sourcePlug = pathsPlug()->source(); if( sourcePlug->direction() == Plug::Out && IECore::runTimeCast<const ComputeNode>( sourcePlug->node() ) ) { // pathsPlug() is receiving data from a plug whose value is context varying, meaning // we need to use the intermediate pathMatcherPlug() in computeMatch() instead: m_pathMatcher = nullptr; } else { // pathsPlug() value is not context varying, meaning we can save on graph evaluations // by just precomputing it here and directly using it in computeMatch(): ConstStringVectorDataPtr paths = pathsPlug()->getValue(); m_pathMatcher = new PathMatcherData; m_pathMatcher->writable().init( paths->readable().begin(), paths->readable().end() ); } } }
void DependencyNode::propagateDirtiness( Plug *plugToDirty ) { // we're not able to signal anything if there's no node, so just early out Node *node = plugToDirty->ancestor<Node>(); if( !node ) { return; } // we don't emit dirtiness immediately for each plug as we traverse the // dependency graph for two reasons : // // - we don't want to emit dirtiness for the same plug more than once // - we don't want to emit dirtiness while the graph may still be being // rewired by slots connected to plugSetSignal() or plugInputChangedSignal() // // instead we collect all the dirty plugs in a container as we traverse // the graph and only when the traversal is complete do we emit the plugDirtiedSignal(). // // the container used is stored per-thread as although it's illegal to be // monkeying with a script from multiple threads, it's perfectly legal to // be monkeying with a different script in each thread. DirtyPlugsContainer &dirtyPlugs = g_dirtyPlugsContainers.local(); // if the container is currently empty then we are at the start of a traversal, // and will emit plugDirtiedSignal() and empty the container before returning // from this function. if the container isn't empty then we are mid-traversal // and will just add to it. const bool emit = dirtyPlugs.empty(); Plug *p = plugToDirty; while( p ) { dirtyPlugs.insert( p ); p = p->parent<Plug>(); } // we only propagate dirtiness along leaf level plugs, because // they are the only plugs which can be the target of the affects(), // and compute() methods. if( !plugToDirty->isInstanceOf( (IECore::TypeId)CompoundPlugTypeId ) ) { DependencyNode *dependencyNode = IECore::runTimeCast<DependencyNode>( node ); if( dependencyNode ) { AffectedPlugsContainer affected; dependencyNode->affects( plugToDirty, affected ); for( AffectedPlugsContainer::const_iterator it=affected.begin(); it!=affected.end(); it++ ) { if( ( *it )->isInstanceOf( (IECore::TypeId)Gaffer::CompoundPlugTypeId ) ) { // DependencyNode::affects() implementations are only allowed to place leaf plugs in the outputs, // so we helpfully report any mistakes. dirtyPlugs.clear(); throw IECore::Exception( "Non-leaf plug " + (*it)->fullName() + " cannot be returned by affects()" ); } // cast is ok - AffectedPlugsContainer only holds const pointers so that // affects() can be const to discourage implementations from having side effects. propagateDirtiness( const_cast<Plug *>( *it ) ); } } for( Plug::OutputContainer::const_iterator it=plugToDirty->outputs().begin(), eIt=plugToDirty->outputs().end(); it!=eIt; ++it ) { propagateDirtiness( *it ); } } if( emit ) { for( size_t i = 0, e = dirtyPlugs.size(); i < e; ++i ) { Plug *plug = dirtyPlugs.get<1>()[i]; Node *node = plug->node(); if( node ) { node->plugDirtiedSignal()( plug ); } } dirtyPlugs.clear(); } }
BoxPtr Box::create( Node *parent, const Set *childNodes ) { BoxPtr result = new Box; parent->addChild( result ); // it's pretty natural to call this function passing childNodes == ScriptNode::selection(). // unfortunately nodes will be removed from the selection as we reparent // them, so we have to make a copy of childNodes so our iteration isn't befuddled by // the changing contents. we can use this opportunity to weed out anything in childNodes // which isn't a direct child of parent though. StandardSetPtr verifiedChildNodes = new StandardSet(); for( NodeIterator nodeIt( parent ); nodeIt != nodeIt.end(); nodeIt++ ) { if( childNodes->contains( nodeIt->get() ) ) { verifiedChildNodes->add( *nodeIt ); } } // when a node we're putting in the box has connections to // a node remaining outside, we need to reroute the connection // via an intermediate plug on the box. this mapping maps input // plugs (be they internal or external) to intermediate input plugs. typedef std::pair<const Plug *, Plug *> PlugPair; typedef std::map<const Plug *, Plug *> PlugMap; PlugMap plugMap; for( size_t i = 0, e = verifiedChildNodes->size(); i < e; i++ ) { Node *childNode = static_cast<Node *>( verifiedChildNodes->member( i ) ); // reroute any connections to external nodes for( RecursivePlugIterator plugIt( childNode ); plugIt != plugIt.end(); plugIt++ ) { Plug *plug = plugIt->get(); if( plug->direction() == Plug::In ) { Plug *input = plug->getInput<Plug>(); if( input && !verifiedChildNodes->contains( input->node() ) ) { PlugMap::const_iterator mapIt = plugMap.find( input ); if( mapIt == plugMap.end() ) { PlugPtr intermediateInput = plug->createCounterpart( result->promotedCounterpartName( plug ), Plug::In ); // we want intermediate inputs to appear on the same side of the node as the // equivalent internal plug, so we copy the relevant metadata over. copyMetadata( plug, intermediateInput.get() ); intermediateInput->setFlags( Plug::Dynamic, true ); result->addChild( intermediateInput ); intermediateInput->setInput( input ); mapIt = plugMap.insert( PlugPair( input, intermediateInput.get() ) ).first; } plug->setInput( mapIt->second ); plugIt.prune(); } } else { // take a copy of the outputs, because we might be modifying the // original as we iterate. Plug::OutputContainer outputs = plug->outputs(); if( !outputs.empty() ) { typedef Plug::OutputContainer::const_iterator OutputIterator; for( OutputIterator oIt = outputs.begin(), eIt = outputs.end(); oIt != eIt; oIt++ ) { Plug *output = *oIt; const Node *outputNode = output->node(); if( outputNode->parent<Node>() == parent && !verifiedChildNodes->contains( outputNode ) ) { PlugMap::const_iterator mapIt = plugMap.find( plug ); if( mapIt == plugMap.end() ) { PlugPtr intermediateOutput = plug->createCounterpart( result->promotedCounterpartName( plug ), Plug::Out ); copyMetadata( plug, intermediateOutput.get() ); intermediateOutput->setFlags( Plug::Dynamic, true ); result->addChild( intermediateOutput ); intermediateOutput->setInput( plug ); mapIt = plugMap.insert( PlugPair( plug, intermediateOutput.get() ) ).first; } output->setInput( mapIt->second ); } } plugIt.prune(); } } } // reparent the child under the Box. it's important that we do this after adding the intermediate // input plugs, so that when they are serialised and reloaded, the inputs to the box are set before // the inputs to the nodes inside the box - see GafferSceneTest.ShaderAssignmentTest.testAssignShaderFromOutsideBox // for a test case highlighting this necessity. result->addChild( childNode ); } return result; }