TuttleOFX
1
|
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