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 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(); } }