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