void ScriptNode::deleteNodes( Node *parent, const Set *filter, bool reconnect ) { parent = parent ? parent : this; // because children are stored as a vector, it's // much more efficient to delete those at the end before // those at the beginning. int i = (int)(parent->children().size()) - 1; while( i >= 0 ) { Node *node = parent->getChild<Node>( i ); if( node && ( !filter || filter->contains( node ) ) ) { // reconnect the inputs and outputs as though the node was disabled DependencyNode *dependencyNode = IECore::runTimeCast<DependencyNode>( node ); if( reconnect && dependencyNode ) { for( OutputPlugIterator it( node ); it != it.end(); ++it ) { Plug *inPlug = dependencyNode->correspondingInput( it->get() ); if ( !inPlug ) { continue; } Plug *srcPlug = inPlug->getInput<Plug>(); if ( !srcPlug ) { continue; } // record this plug's current outputs, and reconnect them. This is a copy of (*it)->outputs() rather // than a reference, as reconnection can modify (*it)->outputs()... Plug::OutputContainer outputs = (*it)->outputs(); for ( Plug::OutputContainer::const_iterator oIt = outputs.begin(); oIt != outputs.end(); ) { Plug *dstPlug = *oIt; if ( dstPlug && dstPlug->acceptsInput( srcPlug ) && this->isAncestorOf( dstPlug ) ) { oIt++; dstPlug->setInput( srcPlug ); } else { oIt++; } } } } parent->removeChild( node ); } i--; } }
void buildInParallel(vector<string> filesToBuild,StringToDepNodeMap dnMap,map<string,bool> FileStatus){ graph g; broadcast_node<continue_msg> input(g); map<string,continue_node<continue_msg>* > continueNodes; for(int ii = 0; ii < filesToBuild.size(); ii++){ string name = filesToBuild[ii]; DependencyNode* node = dnMap[name]; bool fileHasChangedOnDisk = FileStatus[name]; continue_node<continue_msg> * f = new continue_node<continue_msg>( g, [&FileStatus,node,name,fileHasChangedOnDisk]( const continue_msg& ){ bool parentsChanged = checkIfParentsHaveChanged(node->dependencies,FileStatus); bool needToBuild = fileHasChangedOnDisk || parentsChanged; if(needToBuild){ node->doBuild(); } tbb::mutex::scoped_lock lock; lock.acquire(mut); FileStatus[name] = needToBuild; lock.release(); }); continueNodes[node->target]= f; } map<string,continue_node<continue_msg>* >::iterator iter; for(iter = continueNodes.begin(); iter != continueNodes.end(); iter++){ DependencyNode* depNode = dnMap[iter->first]; continue_node<continue_msg>*node = iter->second; //if it is a leaf node, connect it to the input node, else connect it to its parents if(depNode->dependencies.size() == 0){ make_edge(input,*node); } for(int i = 0; i < depNode->dependencies.size(); i++){ continue_node<continue_msg>* parent = continueNodes[ depNode->dependencies[i]->target ]; make_edge(*parent,*node); } } input.try_put( continue_msg() ); g.wait_for_all(); }
void StandardNodeGadget::updateNodeEnabled( const Gaffer::Plug *dirtiedPlug ) { DependencyNode *dependencyNode = IECore::runTimeCast<DependencyNode>( node() ); if( !dependencyNode ) { return; } const Gaffer::BoolPlug *enabledPlug = dependencyNode->enabledPlug(); if( !enabledPlug ) { return; } if( dirtiedPlug && dirtiedPlug != enabledPlug ) { return; } bool enabled = true; try { enabled = enabledPlug->getValue(); } catch( const std::exception &e ) { // The error will be reported via Node::errorSignal() anyway. return; } if( enabled == m_nodeEnabled ) { return; } m_nodeEnabled = enabled; requestRender(); }
void Plug::propagateDirtiness() { if( children().size() ) /// \todo This would be isInstanceOf( CompoundPlugTypeId ) if it didn't cause crashes somehow { // 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. return; } DependencyNode *n = ancestor<DependencyNode>(); if( n ) { if( direction()==In ) { DependencyNode::AffectedPlugsContainer affected; n->affects( this, affected ); for( DependencyNode::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. throw IECore::Exception( "Non-leaf plug " + (*it)->fullName() + " cannot be returned by affects()" ); } const_cast<Plug *>( *it )->emitDirtiness( n ); const_cast<Plug *>( *it )->propagateDirtiness(); } } } for( OutputContainer::const_iterator it=outputs().begin(); it!=outputs().end(); it++ ) { (*it)->emitDirtiness(); (*it)->propagateDirtiness(); } }
void ScriptNode::deleteNodes( Node *parent, const Set *filter, bool reconnect ) { parent = parent ? parent : this; // because children are stored as a vector, it's // much more efficient to delete those at the end before // those at the beginning. int i = (int)(parent->children().size()) - 1; while( i >= 0 ) { Node *node = parent->getChild<Node>( i ); if( node && ( !filter || filter->contains( node ) ) ) { // reconnect the inputs and outputs as though the node was disabled DependencyNode *dependencyNode = IECore::runTimeCast<DependencyNode>( node ); if( reconnect && dependencyNode ) { for( RecursiveOutputPlugIterator it( node ); !it.done(); ++it ) { Plug *inPlug = nullptr; try { inPlug = dependencyNode->correspondingInput( it->get() ); } catch( const std::exception &e ) { msg( IECore::Msg::Warning, boost::str( boost::format( "correspondingInput error while deleting - cannot reconnect \"%s\"" ) % it->get()->fullName() ), e.what() ); } if ( !inPlug ) { continue; } Plug *srcPlug = inPlug->getInput(); if ( !srcPlug ) { continue; } // record this plug's current outputs, and reconnect them. This is a copy of (*it)->outputs() rather // than a reference, as reconnection can modify (*it)->outputs()... Plug::OutputContainer outputs = (*it)->outputs(); for ( Plug::OutputContainer::const_iterator oIt = outputs.begin(); oIt != outputs.end(); ++oIt ) { Plug *dstPlug = *oIt; if ( dstPlug && dstPlug->acceptsInput( srcPlug ) && this->isAncestorOf( dstPlug ) ) { dstPlug->setInput( srcPlug ); } } } } parent->removeChild( node ); } i--; } }
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(); } }