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