TuttleOFX  1
TuttleOFX/libraries/tuttle/src/tuttle/host/graph/ProcessGraph.cpp
Go to the documentation of this file.
00001 #include "ProcessGraph.hpp"
00002 #include "ProcessVisitors.hpp"
00003 #include <tuttle/common/utils/color.hpp>
00004 #include <tuttle/host/graph/GraphExporter.hpp>
00005 
00006 #include <boost/foreach.hpp>
00007 
00008 #if(TUTTLE_EXPORT_WITH_TIMER)
00009 #include <boost/timer/timer.hpp>
00010 #endif
00011 
00012 namespace tuttle {
00013 namespace host {
00014 namespace graph {
00015 
00016 const std::string ProcessGraph::_outputId( "TUTTLE_FAKE_OUTPUT" );
00017 
00018 ProcessGraph::ProcessGraph( const ComputeOptions& options, Graph& userGraph, const std::list<std::string>& outputNodes, memory::IMemoryCache& internMemoryCache )
00019         : _instanceCount( userGraph.getInstanceCount() )
00020         , _options(options)
00021         , _internMemoryCache(internMemoryCache)
00022         , _procOptions(&_internMemoryCache)
00023 {
00024         _procOptions._interactive = _options.getIsInteractive();
00025         // imageEffect specific...
00026         _procOptions._renderScale = _options.getRenderScale();
00027         
00028         updateGraph( userGraph, outputNodes );
00029 }
00030 
00031 ProcessGraph::~ProcessGraph()
00032 {}
00033 
00034 
00035 ProcessGraph::VertexAtTime::Key ProcessGraph::getOutputKeyAtTime( const OfxTime time )
00036 {
00037         return VertexAtTime(Vertex(_procOptions, _outputId), time).getKey();
00038 }
00039 
00040 ProcessGraph::InternalGraphAtTimeImpl::vertex_descriptor ProcessGraph::getOutputVertexAtTime( const OfxTime time )
00041 {
00042         return _renderGraphAtTime.getVertexDescriptor( getOutputKeyAtTime( time ) );
00043 }
00044 
00045 /**
00046  * @brief After copying Vertices, we need to duplicate Nodes and relink Vertices with new Nodes.
00047  */
00048 void ProcessGraph::relink()
00049 {
00050         _renderGraph.removeUnconnectedVertices( _renderGraph.getVertexDescriptor( _outputId ) );
00051 
00052         BOOST_FOREACH( InternalGraphImpl::vertex_descriptor vd, _renderGraph.getVertices() )
00053         {
00054                 Vertex& v = _renderGraph.instance( vd );
00055 
00056                 // fake node has no ProcessNode
00057                 if( !v.isFake() )
00058                 {
00059 #ifdef PROCESSGRAPH_USE_LINK
00060                         tuttle::host::INode& origNode = v.getProcessNode(); // pointer of the copied graph, we don't own it !
00061 #else
00062                         const tuttle::host::INode& origNode = v.getProcessNode(); // pointer of the copied graph, we don't own it !
00063 #endif
00064                         std::string key( origNode.getName() );
00065                         NodeMap::iterator it = _nodes.find( key );
00066                         tuttle::host::INode* newNode;
00067                         if( it != _nodes.end() )
00068                         {
00069                                 newNode = it->second;
00070                         }
00071                         else
00072                         {
00073 #ifdef PROCESSGRAPH_USE_LINK
00074                                 newNode = &origNode;
00075                                 _nodes[key] = dynamic_cast<Node*>( newNode ); // link to the original node
00076 #else
00077                                 newNode = origNode.clone();
00078                                 /// @todo tuttle: no dynamic_cast here, _nodes must use tuttle::host::Node
00079                                 _nodes.insert( key, dynamic_cast<Node*>( newNode ) ); // owns the new pointer
00080 #endif
00081                         }
00082                         // our vertices have a link to our Nodes
00083                         v.setProcessNode( newNode );
00084                 }
00085         }
00086 }
00087 
00088 /*
00089    void removeVertexAndReconnectTo( const VertexDescriptor& v, const VertexDescriptor& other )
00090    {
00091     InternalGraph::out_edge_iterator oe, oeEnd;
00092     tie(oe, oeEnd) = out_edges(v, g);
00093     // InternalGraph::in_edge_iterator ie, ieEnd;
00094     // tie(ie, ieEnd) = in_edges(v, g);
00095 
00096     for( ; oe != oeEnd; ++oe )
00097         source( oe )
00098 
00099     _renderGraph.removeVertex( v );
00100    }
00101  */
00102 
00103 /*
00104    // May be interesting for process function.
00105    typedef std::vector< Vertex > container;
00106    container c;
00107    topological_sort( G, std::back_inserter(c) );
00108 
00109    //cout << "A topological ordering: ";
00110    //for( container::reverse_iterator ii=c.rbegin(); ii!=c.rend(); ++ii )
00111    //cout << index(*ii) << " ";
00112    //cout << endl;
00113 */
00114 /*
00115 template<class TGraph>
00116 class SortEdgeByMemorySize
00117 {
00118 public:
00119         typedef typename TGraph::GraphContainer GraphContainer;
00120         typedef typename TGraph::Vertex Vertex;
00121         typedef typename TGraph::Edge Edge;
00122         typedef typename TGraph::edge_descriptor edge_descriptor;
00123 
00124         SortEdgeByMemorySize( const TGraph& graph )
00125                 : _renderGraph( graph )
00126         {}
00127 
00128         inline bool operator()( const edge_descriptor& ed1, const edge_descriptor& ed2 ) const
00129         {
00130                 const Vertex& v1 = _renderGraph.targetInstance( ed1 );
00131                 const Vertex& v2 = _renderGraph.targetInstance( ed2 );
00132 
00133                 bool res= v1.getProcessDataAtTime()._globalInfos._memory < v2.getProcessDataAtTime()._globalInfos._memory;
00134 //              TUTTLE_LOG_VAR2(v1.getName(), v1.getProcessDataAtTime()._globalInfos._memory);
00135 //              TUTTLE_LOG_VAR2(v2.getName(), v2.getProcessDataAtTime()._globalInfos._memory);
00136 //              TUTTLE_LOG_VAR(res);
00137                 return res;
00138         }
00139 private:
00140         const TGraph& _renderGraph;
00141 };
00142 */
00143 
00144 void ProcessGraph::bakeGraphInformationToNodes( InternalGraphAtTimeImpl& _renderGraphAtTime )
00145 {
00146         BOOST_FOREACH( const InternalGraphAtTimeImpl::vertex_descriptor vd, _renderGraphAtTime.getVertices() )
00147         {
00148                 VertexAtTime& v = _renderGraphAtTime.instance( vd );
00149                 ProcessVertexAtTimeData& vData = v.getProcessDataAtTime();
00150                 
00151                 TUTTLE_TLOG( TUTTLE_INFO, "[bake graph information to nodes] node: " << v.getName() );
00152 
00153                 vData._outDegree = _renderGraphAtTime.getInDegree( vd );
00154                 vData._inDegree = _renderGraphAtTime.getOutDegree( vd );
00155 
00156                 vData._outEdges.clear();
00157                 vData._outEdges.reserve( vData._outDegree );
00158                 BOOST_FOREACH( const InternalGraphAtTimeImpl::edge_descriptor ed, _renderGraphAtTime.getInEdges( vd ) )
00159                 {
00160                         const ProcessEdgeAtTime* e = &_renderGraphAtTime.instance(ed);
00161 
00162                         TUTTLE_TLOG( TUTTLE_INFO, "[bake graph information to nodes] in edge " << e->getInAttrName() << ", at time " << e->getInTime() );
00163                         vData._outEdges.push_back( e );
00164                 }
00165                 vData._inEdges.clear();
00166                 BOOST_FOREACH( const InternalGraphAtTimeImpl::edge_descriptor ed, _renderGraphAtTime.getOutEdges( vd ) )
00167                 {
00168                         const ProcessEdgeAtTime* e = &_renderGraphAtTime.instance(ed);
00169                         TUTTLE_TLOG( TUTTLE_INFO, "[bake graph information to nodes] out edge " << e->getInAttrName() << ", at time " << e->getInTime() );
00170                         std::pair<std::string, OfxTime> key( e->getInAttrName(), e->getInTime() );
00171                         vData._inEdges[key] = e;
00172                 }
00173         }
00174         TUTTLE_TLOG( TUTTLE_INFO, "[bake graph information to nodes] connect clips" );
00175         connectClips<InternalGraphAtTimeImpl>( _renderGraphAtTime );
00176 
00177 }
00178 
00179 void ProcessGraph::beginSequence( const TimeRange& timeRange )
00180 {
00181         _options.beginSequenceHandle();
00182         _procOptions._renderTimeRange.min = timeRange._begin;
00183         _procOptions._renderTimeRange.max = timeRange._end;
00184         _procOptions._step                = timeRange._step;
00185 
00186         TUTTLE_TLOG( TUTTLE_INFO, "[begin sequence] start" );
00187         //      BOOST_FOREACH( NodeMap::value_type& p, _nodes )
00188         for( NodeMap::iterator it = _nodes.begin(), itEnd = _nodes.end();
00189                 it != itEnd;
00190                 ++it )
00191         {
00192                 NodeMap::value_type& p = *it;
00193                 p.second->beginSequence( _procOptions );
00194         }
00195 }
00196 
00197 void ProcessGraph::endSequence()
00198 {
00199         _options.endSequenceHandle();
00200         TUTTLE_TLOG( TUTTLE_INFO, "[Process render] process end sequence" );
00201         //--- END sequence render
00202         BOOST_FOREACH( NodeMap::value_type& p, _nodes )
00203         {
00204                 p.second->endSequence( _procOptions ); // node option... or no option here ?
00205         }
00206 }
00207 
00208 void ProcessGraph::updateGraph( Graph& userGraph, const std::list<std::string>& outputNodes )
00209 {
00210         _renderGraph.copyTransposed( userGraph.getGraph() );
00211 
00212         Vertex outputVertex( _procOptions, _outputId );
00213 
00214         if( outputNodes.size() )
00215         {
00216                 _renderGraph.addVertex( outputVertex );
00217                 BOOST_FOREACH( const std::string & s, outputNodes )
00218                 {
00219                         _renderGraph.connect( _outputId, s, "Output" );
00220                         TUTTLE_LOG_DEBUG( TUTTLE_INFO, "MY OUTPUT: " << s );
00221                 }
00222         }
00223         else
00224         {
00225                 // Detect root nodes and add them to the list of nodes to process
00226                 std::vector<InternalGraphImpl::vertex_descriptor> rootVertices = _renderGraph.rootVertices();
00227                 _renderGraph.addVertex( outputVertex );
00228                 BOOST_FOREACH( const InternalGraphImpl::vertex_descriptor vd, rootVertices )
00229                 {
00230                         InternalGraphImpl::VertexKey vk = _renderGraph.instance( vd ).getKey();
00231                         _renderGraph.connect( _outputId, vk, "Output" );
00232                 }
00233         }
00234         
00235         relink();
00236 }
00237 
00238 void ProcessGraph::setup()
00239 {
00240         using namespace boost;
00241         using namespace boost::graph;
00242         TUTTLE_TLOG( TUTTLE_INFO, "[Process render] setup" );
00243         
00244         // Initialize variables
00245 //      OfxRectD renderWindow = { 0, 0, 0, 0 };
00246 
00247         //--- BEGIN RENDER
00248 
00249         ///@todo tuttle: exception if there is non-optional clips unconnected.
00250         /// It's already checked in the beginSequence of the imageEffectNode.
00251         /// But maybe it could better to check that here independently from node types.
00252 //      graph::visitor::UnconnectedClips<InternalGraphImpl> unconnectedClipsVisitor( _renderGraph );
00253 //      _renderGraph.depthFirstSearch( unconnectedClipsVisitor );
00254 //      if( unconnectedClipsVisitor.value )
00255 //      {
00256 //              exception::user userMsg("Some non optional clips are unconnected. We can't do the process.\n");
00257 //              userMsg << "Unconnected clips : ";
00258 //              BOOST_FOREACH( clip, unconnectedClipsVisitor.clips )
00259 //              {
00260 //                      userMsg << clip->getFullName() << ",";
00261 //              }
00262 //              userMsg << std::endl;
00263 //              BOOST_THROW_EXCEPTION( exception::Logic()
00264 //                      << userMsg );
00265 //      }
00266 
00267         BOOST_FOREACH( InternalGraphImpl::vertex_descriptor vd, _renderGraph.getVertices() )
00268         {
00269                 Vertex& v = _renderGraph.instance(vd);
00270                 if( ! v.isFake() )
00271                 {
00272                         v.setProcessData( _procOptions );
00273                         v.getProcessNode().setProcessData( &v._data );
00274                 }
00275         }
00276         
00277         connectClips<InternalGraphImpl>( _renderGraph );
00278         
00279         {
00280                 TUTTLE_TLOG( TUTTLE_INFO, "[Process render] Time domain propagation" );
00281                 graph::visitor::TimeDomain<InternalGraphImpl> timeDomainPropagationVisitor( _renderGraph );
00282                 _renderGraph.depthFirstVisit( timeDomainPropagationVisitor, _renderGraph.getVertexDescriptor( _outputId ) );
00283         }
00284 
00285         {       
00286                 TUTTLE_TLOG( TUTTLE_INFO, "[Process render] setup visitors" );
00287                 graph::visitor::Setup1<InternalGraphImpl> setup1Visitor( _renderGraph );
00288                 _renderGraph.depthFirstVisit( setup1Visitor, _renderGraph.getVertexDescriptor( _outputId ) );
00289                 graph::visitor::Setup2<InternalGraphImpl> setup2Visitor( _renderGraph );
00290                 _renderGraph.depthFirstVisit( setup2Visitor, _renderGraph.getVertexDescriptor( _outputId ) );
00291                 graph::visitor::Setup3<InternalGraphImpl> setup3Visitor( _renderGraph );
00292                 _renderGraph.depthFirstVisit( setup3Visitor, _renderGraph.getVertexDescriptor( _outputId ) );
00293         }
00294 }
00295 
00296 std::list<TimeRange> ProcessGraph::computeTimeRange()
00297 {
00298         std::list<TimeRange> timeRanges = _options.getTimeRanges();
00299 
00300         TUTTLE_TLOG_INFOS;
00301         if( timeRanges.empty() )
00302         {
00303                 BOOST_FOREACH( InternalGraphImpl::edge_descriptor ed, boost::out_edges( _renderGraph.getVertexDescriptor(_outputId), _renderGraph.getGraph() ) )
00304                 {
00305                         //TUTTLE_TLOG_INFOS;
00306                         ProcessVertex& v = _renderGraph.targetInstance( ed );
00307                         // compute the time domain for each output node
00308                         //TUTTLE_TLOG_INFOS;
00309                         OfxRangeD timeDomain = v.getProcessData()._timeDomain;
00310                         TUTTLE_TLOG_VAR2( TUTTLE_TRACE, timeDomain.min, timeDomain.max );
00311                         
00312                         if( _options.getBegin() != std::numeric_limits<int>::min() && timeDomain.min < _options.getBegin() )
00313                                 timeDomain.min = _options.getBegin();
00314                         if( _options.getEnd() != std::numeric_limits<int>::max() && timeDomain.max > _options.getEnd() )
00315                                 timeDomain.max = _options.getEnd();
00316                         
00317                         TUTTLE_TLOG_VAR2( TUTTLE_TRACE, timeDomain.min, timeDomain.max );
00318                         // special case for infinite time domain (eg. a still image)
00319                         if( timeDomain.min <= kOfxFlagInfiniteMin )
00320                                 timeDomain.min = 0;
00321                         if( timeDomain.max >= kOfxFlagInfiniteMax )
00322                                 timeDomain.max = 0;
00323 
00324                         //TUTTLE_TLOG_INFOS;
00325                         timeRanges.push_back( TimeRange( timeDomain ) );
00326                         TUTTLE_TLOG_INFOS;
00327                         TUTTLE_TLOG( TUTTLE_INFO, "Compute " << quotes(v.getName()) << " full time domain: from " << timeDomain.min << " to " << timeDomain.max << "." );
00328                 }
00329         }
00330         return timeRanges;
00331 }
00332 
00333 void ProcessGraph::setupAtTime( const OfxTime time )
00334 {
00335         _options.setupAtTimeHandle();
00336 #if(TUTTLE_EXPORT_WITH_TIMER)
00337         boost::timer::cpu_timer timer;
00338 #endif
00339         
00340         TUTTLE_LOG_TRACE( "[Setup at time " << time << "] start" );
00341         graph::visitor::DeployTime<InternalGraphImpl> deployTimeVisitor( _renderGraph, time );
00342         _renderGraph.depthFirstVisit( deployTimeVisitor, _renderGraph.getVertexDescriptor( _outputId ) );
00343 #if(TUTTLE_EXPORT_PROCESSGRAPH_DOT)
00344         graph::exportDebugAsDOT( "graphProcess_c.dot", _renderGraph );
00345 #endif
00346 
00347         TUTTLE_LOG_TRACE( "[Setup at time " << time << "] build render graph" );
00348         // create a new graph with time information
00349         _renderGraphAtTime.clear();
00350         
00351         {
00352                 BOOST_FOREACH( InternalGraphAtTimeImpl::vertex_descriptor vd, _renderGraph.getVertices() )
00353                 {
00354                         Vertex& v = _renderGraph.instance( vd );
00355                         BOOST_FOREACH( const OfxTime t, v._data._times )
00356                         {
00357                                 TUTTLE_TLOG( TUTTLE_INFO, "[Setup at time " << time << "] add connection from node: " << v << " for time: " << t );
00358                                 _renderGraphAtTime.addVertex( ProcessVertexAtTime(v, t) );
00359                         }
00360                 }
00361                 BOOST_FOREACH( const InternalGraphAtTimeImpl::edge_descriptor ed, _renderGraph.getEdges() )
00362                 {
00363                         const Edge& e = _renderGraph.instance( ed );
00364                         const Vertex& in = _renderGraph.sourceInstance( ed );
00365                         const Vertex& out = _renderGraph.targetInstance( ed );
00366                         TUTTLE_TLOG( TUTTLE_INFO, "[Setup at time " << time << "] set connection " << e );
00367                         BOOST_FOREACH( const Edge::TimeMap::value_type& tm, e._timesNeeded )
00368                         {
00369                                 const VertexAtTime procIn( in, tm.first );
00370                                 BOOST_FOREACH( const OfxTime t2, tm.second )
00371                                 {
00372                                         //TUTTLE_TLOG_VAR( TUTTLE_TRACE, tm.first );
00373                                         //TUTTLE_TLOG_VAR( TUTTLE_TRACE, t2 );
00374                                         const VertexAtTime procOut( out, t2 );
00375 
00376                                         const VertexAtTime::Key inKey( procIn.getKey() );
00377                                         const VertexAtTime::Key outKey( procOut.getKey() );
00378 
00379                                         //TUTTLE_TLOG_VAR( TUTTLE_TRACE, inKey );
00380                                         //TUTTLE_TLOG_VAR( TUTTLE_TRACE, outKey );
00381                                         //TUTTLE_TLOG_VAR( TUTTLE_TRACE, e.getInAttrName() );
00382 
00383                                         const EdgeAtTime eAtTime( outKey, inKey, e.getInAttrName() );
00384 
00385                                         _renderGraphAtTime.addEdge(
00386                                                 _renderGraphAtTime.getVertexDescriptor( inKey ),
00387                                                 _renderGraphAtTime.getVertexDescriptor( outKey ),
00388                                                 eAtTime );
00389                                 }
00390                         }
00391                 }
00392         }
00393 
00394         InternalGraphAtTimeImpl::vertex_descriptor outputAtTime = getOutputVertexAtTime( time );
00395         
00396         // declare final nodes
00397         BOOST_FOREACH( const InternalGraphAtTimeImpl::edge_descriptor ed, boost::out_edges( outputAtTime, _renderGraphAtTime.getGraph() ) )
00398         {
00399                 VertexAtTime& v = _renderGraphAtTime.targetInstance( ed );
00400                 v.getProcessDataAtTime()._isFinalNode = true; /// @todo: this is maybe better to move this into the ProcessData? Doesn't depend on time?
00401         }
00402 
00403         TUTTLE_TLOG( TUTTLE_INFO, "[Setup at time " << time << "] set data at time" );
00404         // give a link to the node on its attached process data
00405         BOOST_FOREACH( const InternalGraphAtTimeImpl::vertex_descriptor vd, _renderGraphAtTime.getVertices() )
00406         {
00407                 VertexAtTime& v = _renderGraphAtTime.instance(vd);
00408                 if( ! v.isFake() )
00409                 {
00410                         //TUTTLE_TLOG( TUTTLE_INFO, "setProcessDataAtTime: " << v._name << " id: " << v._id << " at time: " << v._data._time );
00411                         v.getProcessNode().setProcessDataAtTime( &v._data );
00412                 }
00413         }
00414 
00415         bakeGraphInformationToNodes( _renderGraphAtTime );
00416 
00417 
00418 #if(TUTTLE_EXPORT_PROCESSGRAPH_DOT)
00419         graph::exportDebugAsDOT( "graphProcessAtTime_a.dot", _renderGraphAtTime );
00420 #endif
00421 
00422         if( ! _options.getForceIdentityNodesProcess() )
00423         {
00424                 TUTTLE_LOG_TRACE( "[Setup at time " << time << "] remove identity nodes" );
00425                 // The "Remove identity nodes" step need to be done after preprocess steps, because the RoI need to be computed.
00426                 std::vector<graph::visitor::IdentityNodeConnection<InternalGraphAtTimeImpl> > toRemove;
00427 
00428                 graph::visitor::RemoveIdentityNodes<InternalGraphAtTimeImpl> vis( _renderGraphAtTime, toRemove );
00429                 _renderGraphAtTime.depthFirstVisit( vis, outputAtTime );
00430                 TUTTLE_LOG_TRACE( "[Setup at time " << time << "] removing " << toRemove.size() << " nodes" );
00431                 if( toRemove.size() )
00432                 {
00433                         graph::visitor::removeIdentityNodes( _renderGraphAtTime, toRemove );
00434 
00435                         // Bake graph information again as the connections have changed.
00436                         bakeGraphInformationToNodes( _renderGraphAtTime );
00437                 }
00438         }
00439 
00440 #if(TUTTLE_EXPORT_PROCESSGRAPH_DOT)
00441         graph::exportDebugAsDOT( "graphProcessAtTime_b.dot", _renderGraphAtTime );
00442 #endif
00443 
00444         {
00445                 TUTTLE_LOG_TRACE( "[Setup at time " << time << "] preprocess 1" );
00446                 graph::visitor::PreProcess1<InternalGraphAtTimeImpl> preProcess1Visitor( _renderGraphAtTime );
00447                 _renderGraphAtTime.depthFirstVisit( preProcess1Visitor, outputAtTime );
00448         }
00449 
00450         {
00451                 TUTTLE_LOG_TRACE( "[Setup at time " << time << "] preprocess 2" );
00452                 graph::visitor::PreProcess2<InternalGraphAtTimeImpl> preProcess2Visitor( _renderGraphAtTime );
00453                 _renderGraphAtTime.depthFirstVisit( preProcess2Visitor, outputAtTime );
00454         }
00455 
00456 #if(TUTTLE_EXPORT_PROCESSGRAPH_DOT)
00457         graph::exportDebugAsDOT( "graphProcessAtTime_c.dot", _renderGraphAtTime );
00458 #endif
00459 
00460         /*
00461         TUTTLE_TLOG( TUTTLE_INFO, "---------------------------------------- optimize graph" );
00462         graph::visitor::OptimizeGraph<InternalGraphAtTimeImpl> optimizeGraphVisitor( _renderGraphAtTime );
00463         _renderGraphAtTime.depthFirstVisit( optimizeGraphVisitor, outputAtTime );
00464         */
00465 #if(TUTTLE_EXPORT_PROCESSGRAPH_DOT)
00466         graph::exportDebugAsDOT( "graphProcessAtTime_d.dot", _renderGraphAtTime );
00467 #endif
00468         /*
00469         InternalGraphImpl tmpGraph;
00470         output = _renderGraph.getVertexDescriptor( _outputId );
00471         /// @todo tuttle: out_edges sort don't work...
00472         TUTTLE_TLOG( TUTTLE_INFO, "---------------------------------------- sorting graph" );
00473         BOOST_FOREACH( InternalGraphImpl::vertex_descriptor vd, _renderGraph.getVertices() )
00474         {
00475                 std::vector<InternalGraphImpl::Edge> edges( boost::out_degree(vd, _renderGraph.getGraph()) );
00476 
00477                 BOOST_FOREACH( InternalGraphImpl::edge_descriptor ed, boost::out_edges( vd, _renderGraph.getGraph() ) )
00478                 {
00479                         edges.push_back( _renderGraph.instance(ed) );
00480                 }
00481 
00482                 Vertex& v = _renderGraph.instance(vd);
00483 
00484                 std::size_t i = 0;
00485                 TUTTLE_TLOG( TUTTLE_INFO, "before sort edges of " << v.getName() );
00486                 BOOST_FOREACH( InternalGraphImpl::edge_descriptor ed, boost::out_edges( vd, _renderGraph.getGraph() ) )
00487                 {
00488                         Edge& e = _renderGraph.instance(ed);
00489                         e._localId = i++;
00490                         e._name += " -- ";
00491                         e._name += boost::lexical_cast<std::string>(e._localId); // tmp
00492                         TUTTLE_TLOG( TUTTLE_INFO, e.getName() << " - " <<  _renderGraph.targetInstance(ed).getProcessDataAtTime()._globalInfos._memory  );
00493                 }
00494                 std::sort( edges.begin(), edges.end(), SortEdgeByMemorySize<InternalGraphImpl>(_renderGraph) );
00495                 TUTTLE_TLOG( TUTTLE_INFO, "after sort edges of " << v.getName() );
00496                 BOOST_FOREACH( InternalGraphImpl::edge_descriptor ed, boost::out_edges( vd, _renderGraph.getGraph() ) )
00497                 {
00498                         Edge& e = _renderGraph.instance(ed);
00499                         TUTTLE_TLOG( TUTTLE_INFO, e.getName() << " - " <<  _renderGraph.targetInstance(ed).getProcessDataAtTime()._globalInfos._memory );
00500                 }
00501                 InternalGraphImpl::out_edge_iterator oe_it, oe_itEnd;
00502                 boost::tie( oe_it, oe_itEnd ) = boost::out_edges( vd, _renderGraph.getGraph() );
00503                 for( ; oe_it != oe_itEnd; ++oe_it )
00504                 {
00505                         Edge& e = _renderGraph.instance(*oe_it);
00506                         TUTTLE_TLOG( TUTTLE_INFO, e.getName() << " - " <<  _renderGraph.targetInstance(*oe_it).getProcessDataAtTime()._globalInfos._memory );
00507                 }
00508         }
00509 #if(TUTTLE_EXPORT_PROCESSGRAPH_DOT)
00510         graph::exportDebugAsDOT( "graphprocess_e.dot", tmpGraph );
00511 #endif
00512         */
00513 
00514 }
00515 
00516 void ProcessGraph::computeHashAtTime( NodeHashContainer& outNodesHash, const OfxTime time )
00517 {
00518 #if(TUTTLE_EXPORT_WITH_TIMER)
00519         boost::timer::cpu_timer timer;
00520 #endif
00521         setupAtTime( time );
00522         TUTTLE_TLOG( TUTTLE_INFO, "[Compute hash at time] begin" );
00523         graph::visitor::ComputeHashAtTime<InternalGraphAtTimeImpl> computeHashAtTimeVisitor( _renderGraphAtTime, outNodesHash, time );
00524         InternalGraphAtTimeImpl::vertex_descriptor outputAtTime = getOutputVertexAtTime( time );
00525         _renderGraphAtTime.depthFirstVisit( computeHashAtTimeVisitor, outputAtTime );
00526         TUTTLE_TLOG( TUTTLE_INFO, "[Compute hash at time] end" );
00527 }
00528 
00529 void ProcessGraph::processAtTime( memory::IMemoryCache& outCache, const OfxTime time )
00530 {
00531         _options.processAtTimeHandle();
00532 #if(TUTTLE_EXPORT_WITH_TIMER)
00533         boost::timer::cpu_timer timer;
00534 #endif
00535         
00536         TUTTLE_LOG_TRACE( "[Process at time " << time << "] Output node : " << _renderGraph.getVertex( _outputId ).getName() );
00537         InternalGraphAtTimeImpl::vertex_descriptor outputAtTime = getOutputVertexAtTime( time );
00538 
00539     // Launch a pass of callbacks on the nodes
00540     graph::visitor::BeforeRenderCallbackVisitor<InternalGraphAtTimeImpl> 
00541         callbackRun( _renderGraphAtTime );
00542     _renderGraphAtTime.depthFirstVisit( callbackRun, outputAtTime );
00543 
00544         // do the process
00545         graph::visitor::Process<InternalGraphAtTimeImpl> processVisitor( _renderGraphAtTime, _internMemoryCache );
00546         if( _options.getReturnBuffers() )
00547         {
00548                 // accumulate output nodes buffers into the @p outCache MemoryCache
00549                 processVisitor.setOutputMemoryCache( outCache );
00550         }
00551 
00552         _renderGraphAtTime.depthFirstVisit( processVisitor, outputAtTime );
00553 
00554         TUTTLE_LOG_TRACE( "[Process at time " << time << "] Post process" );
00555         graph::visitor::PostProcess<InternalGraphAtTimeImpl> postProcessVisitor( _renderGraphAtTime );
00556         _renderGraphAtTime.depthFirstVisit( postProcessVisitor, outputAtTime );
00557 
00558         ///@todo clean datas...
00559         TUTTLE_LOG_TRACE( "[Process at time " << time << "] Clear data at time" );
00560         // give a link to the node on its attached process data
00561         BOOST_FOREACH( const InternalGraphAtTimeImpl::vertex_descriptor vd, _renderGraphAtTime.getVertices() )
00562         {
00563                 VertexAtTime& v = _renderGraphAtTime.instance(vd);
00564                 if( ! v.isFake() )
00565                 {
00566                         v.getProcessNode().clearProcessDataAtTime();
00567                 }
00568         }
00569 
00570         // clear cache at each frame
00571         // @todo: remove
00572         _internMemoryCache.clearUnused();
00573 
00574         TUTTLE_LOG_TRACE( "[Process at time " << time << "] Memory cache size: " << _internMemoryCache.size() );
00575         TUTTLE_LOG_TRACE( "[Process at time " << time << "] Out cache size: " << outCache.size() );
00576 }
00577 
00578 bool ProcessGraph::process( memory::IMemoryCache& outCache )
00579 {
00580 #if(TUTTLE_EXPORT_WITH_TIMER)
00581         boost::timer::cpu_timer all_process_timer;
00582 #endif
00583 
00584 #if(TUTTLE_EXPORT_PROCESSGRAPH_DOT)
00585         graph::exportAsDOT( "graphProcess_a.dot", _renderGraph );
00586 #endif
00587 
00588         setup();
00589 
00590         std::list<TimeRange> timeRanges = computeTimeRange();
00591 
00592 #if(TUTTLE_EXPORT_PROCESSGRAPH_DOT)
00593         graph::exportDebugAsDOT( "graphProcess_b.dot", _renderGraph );
00594 #endif
00595 
00596         /// @todo Bug: need to use a map 'OutputNode': 'timeRanges'
00597         /// And check if all Output nodes share a common timeRange
00598 
00599         TUTTLE_LOG_INFO( "[Process render] start" );
00600 
00601         // Begin range of frames
00602         TimeRange globalTimeRange( timeRanges.back() );
00603         BOOST_FOREACH( const TimeRange& range, timeRanges )
00604         {
00605                 if( globalTimeRange._begin > range._begin )
00606                         globalTimeRange._begin = range._begin;
00607                 if( globalTimeRange._end < range._end )
00608                         globalTimeRange._end = range._end;
00609         }
00610         TUTTLE_LOG_TRACE( "[Process render] begin timeRange: [" << globalTimeRange._begin << ", " << globalTimeRange._end << "]" );
00611         beginSequence( globalTimeRange );
00612 
00613         // RENDER (at each frame)
00614         BOOST_FOREACH( const TimeRange& timeRange, timeRanges )
00615         {
00616                 TUTTLE_LOG_TRACE( "[Process render] process timeRange: [" << timeRange._begin << ", " << timeRange._end << ", " << timeRange._step << "]" );
00617 
00618                 // If someone had asked to abort the process
00619                 if( _options.getAbort() )
00620                 {
00621                         TUTTLE_LOG_ERROR( "[Process render] PROCESS ABORTED before first frame." );
00622                         endSequence();
00623                         _internMemoryCache.clearUnused();
00624                         return false;
00625                 }
00626 
00627                 for( int time = timeRange._begin; time <= timeRange._end; time += timeRange._step )
00628                 {
00629                         _options.beginFrameHandle();
00630 
00631                         try
00632                         {
00633 #if(TUTTLE_EXPORT_WITH_TIMER)
00634                                 boost::timer::cpu_timer setup_timer;
00635 #endif
00636                                 setupAtTime( time );
00637 #if(TUTTLE_EXPORT_WITH_TIMER)
00638                                 TUTTLE_LOG_INFO( "[process timer] setup " << boost::timer::format(setup_timer.elapsed()) );
00639 #endif
00640 
00641 #if(TUTTLE_EXPORT_WITH_TIMER)
00642                                 boost::timer::cpu_timer processAtTime_timer;
00643 #endif
00644                                 processAtTime( outCache, time );
00645 #if(TUTTLE_EXPORT_WITH_TIMER)
00646                                 TUTTLE_LOG_INFO( "[process timer] took " << boost::timer::format(processAtTime_timer.elapsed()) );
00647 #endif
00648                         }
00649                         catch( tuttle::exception::FileInSequenceNotExist& e ) // @todo tuttle: change that.
00650                         {
00651                                 e << tuttle::exception::time(time);
00652                                 if( _options.getContinueOnError() || _options.getContinueOnMissingFile() )
00653                                 {
00654                                         TUTTLE_LOG_WARNING( "[Process render] Missing input file at frame " << time << "." << std::endl
00655                                                         << tuttle::exception::format_exception_message(e) << std::endl
00656                                                         << tuttle::exception::format_exception_info(e)
00657                                                 );
00658                                 }
00659                                 else
00660                                 {
00661                                         TUTTLE_LOG_ERROR( "[Process render] Missing input file at frame " << time << "." << std::endl );
00662                                         _options.endFrameHandle();
00663                                         endSequence();
00664                                         _renderGraphAtTime.clear();
00665                                         _internMemoryCache.clearUnused();
00666                                         throw;
00667                                 }
00668                         }
00669                         catch( ::boost::exception& e )
00670                         {
00671                                 e << tuttle::exception::time(time);
00672                                 if( _options.getContinueOnError() )
00673                                 {
00674                                         TUTTLE_LOG_ERROR( "[Process render] Skip frame " << time << "." << std::endl
00675                                                         << tuttle::exception::format_exception_message(e) << std::endl
00676                                                         << tuttle::exception::format_exception_info(e)
00677                                                 );
00678                                 }
00679                                 else
00680                                 {
00681                                         TUTTLE_LOG_ERROR( "[Process render] Stopped at frame " << time << "." << std::endl );
00682                                         _options.endFrameHandle();
00683                                         endSequence();
00684                                         _renderGraphAtTime.clear();
00685                                         _internMemoryCache.clearUnused();
00686                                         throw;
00687                                 }
00688                         }
00689                         catch(...)
00690                         {
00691                                 if( _options.getContinueOnError() )
00692                                 {
00693                                         TUTTLE_LOG_ERROR( "[Process render] Skip frame " << time << "." << std::endl
00694                                                         << tuttle::exception::format_current_exception()
00695                                                 );
00696                                 }
00697                                 else
00698                                 {
00699                                         TUTTLE_LOG_ERROR( "[Process render] Error at frame " << time << "." << std::endl );
00700                                         _options.endFrameHandle();
00701                                         endSequence();
00702                                         _renderGraphAtTime.clear();
00703                                         _internMemoryCache.clearUnused();
00704                                         throw;
00705                                 }
00706                         }
00707 
00708                         if( _options.getAbort() )
00709                         {
00710                                 TUTTLE_LOG_ERROR( "[Process render] PROCESS ABORTED at time " << time << "." );
00711                                 _options.endFrameHandle();
00712                                 endSequence();
00713                                 _renderGraphAtTime.clear();
00714                                 _internMemoryCache.clearUnused();
00715                                 return false;
00716                         }
00717                         _options.endFrameHandle();
00718                 }
00719         }
00720 
00721         // End range of frames
00722         endSequence();
00723 
00724 #if(TUTTLE_EXPORT_WITH_TIMER)
00725         TUTTLE_LOG_INFO( "[all process timer] " << boost::timer::format(all_process_timer.elapsed()) );
00726 #endif
00727         return true;
00728 }
00729 
00730 }
00731 }
00732 }
00733