Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

331 lines
9.6 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "dependencygraph.h"
  7. #include "datamodel/idatamodel.h"
  8. #include "datamodel/dmelement.h"
  9. #include "mathlib/mathlib.h" // for swap
  10. #include "datamodel/dmattribute.h"
  11. #include "datamodel/dmattributevar.h"
  12. #include "tier1/mempool.h"
  13. #include "tier0/vprof.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. //-----------------------------------------------------------------------------
  17. // Misc helper enums and classes for CDependencyGraph class
  18. //-----------------------------------------------------------------------------
  19. enum TraversalState_t
  20. {
  21. TS_NOT_VISITED,
  22. TS_VISITING,
  23. TS_VISITED,
  24. };
  25. struct COperatorNode
  26. {
  27. COperatorNode( IDmeOperator *pOp = NULL ) :
  28. m_state( TS_NOT_VISITED ),
  29. m_operator( pOp ),
  30. m_bInList( false )
  31. {
  32. }
  33. TraversalState_t m_state;
  34. IDmeOperator *m_operator;
  35. CUtlVector< CAttributeNode * > m_OutputAttributes;
  36. bool m_bInList;
  37. };
  38. class CAttributeNode
  39. {
  40. public:
  41. CAttributeNode( CDmAttribute *attribute = NULL ) :
  42. m_attribute( attribute ),
  43. m_bIsOutputToOperator( false )
  44. {
  45. }
  46. CDmAttribute *m_attribute;
  47. CUtlVector< COperatorNode * > m_InputDependentOperators;
  48. bool m_bIsOutputToOperator;
  49. };
  50. CClassMemoryPool< CAttributeNode > g_AttrNodePool( 1000 );
  51. CClassMemoryPool< COperatorNode > g_OperatorNodePool( 1000 );
  52. bool HashEntryCompareFunc( CAttributeNode *const& lhs, CAttributeNode *const& rhs )
  53. {
  54. return lhs->m_attribute == rhs->m_attribute;
  55. }
  56. uint HashEntryKeyFunc( CAttributeNode *const& keyinfo )
  57. {
  58. uint i = (uint)keyinfo->m_attribute;
  59. return i >> 2; // since memory is allocated on a 4-byte (at least!) boundary
  60. }
  61. //-----------------------------------------------------------------------------
  62. // CDependencyGraph constructor - builds dependency graph from operators
  63. //-----------------------------------------------------------------------------
  64. CDependencyGraph::CDependencyGraph() :
  65. m_attrNodes( 4096, 0, 0, HashEntryCompareFunc, HashEntryKeyFunc )
  66. {
  67. }
  68. void CDependencyGraph::Reset( const CUtlVector< IDmeOperator * > &operators )
  69. {
  70. VPROF_BUDGET( "CDependencyGraph::Reset", VPROF_BUDGETGROUP_TOOLS );
  71. Cleanup();
  72. CUtlVector< CDmAttribute * > attrs; // moved outside the loop to function as a temporary memory pool for performance
  73. int on = operators.Count();
  74. CUtlRBTree< IDmeOperator * > operatorDict( 0, on * 2, DefLessFunc(IDmeOperator *) );
  75. for ( int i = 0; i < on; ++i )
  76. {
  77. operatorDict.Insert( operators[i] );
  78. }
  79. m_opNodes.EnsureCapacity( on );
  80. for ( int oi = 0; oi < on; ++oi )
  81. {
  82. IDmeOperator *pOp = operators[ oi ];
  83. Assert( pOp );
  84. if ( pOp == NULL )
  85. continue;
  86. COperatorNode *pOpNode = g_OperatorNodePool.Alloc();
  87. pOpNode->m_operator = pOp;
  88. attrs.RemoveAll();
  89. pOp->GetInputAttributes( attrs );
  90. int an = attrs.Count();
  91. for ( int ai = 0; ai < an; ++ai )
  92. {
  93. CAttributeNode *pAttrNode = FindAttrNode( attrs[ ai ] );
  94. pAttrNode->m_InputDependentOperators.AddToTail( pOpNode );
  95. }
  96. attrs.RemoveAll();
  97. pOp->GetOutputAttributes( attrs );
  98. an = attrs.Count();
  99. for ( int ai = 0; ai < an; ++ai )
  100. {
  101. CAttributeNode *pAttrNode = FindAttrNode( attrs[ ai ] );
  102. pAttrNode->m_bIsOutputToOperator = true;
  103. pOpNode->m_OutputAttributes.AddToTail( pAttrNode );
  104. #ifdef _DEBUG
  105. // Look for dependent operators, add them if they are not in the array
  106. // FIXME: Should this happen for input attributes too?
  107. CDmElement* pElement = pAttrNode->m_attribute->GetOwner();
  108. IDmeOperator *pOperator = dynamic_cast< IDmeOperator* >( pElement );
  109. if ( pOperator )
  110. {
  111. // Look for the operator in the existing list
  112. if ( operatorDict.Find( pOperator ) == operatorDict.InvalidIndex() )
  113. {
  114. CDmElement *pOp1 = dynamic_cast< CDmElement* >( pOperator );
  115. CDmElement *pOp2 = dynamic_cast< CDmElement* >( pOp );
  116. Warning( "Found dependent operator '%s' referenced by operator '%s' that wasn't in the scene or trackgroups!\n", pOp1->GetName(), pOp2->GetName() );
  117. }
  118. }
  119. #endif
  120. }
  121. m_opNodes.AddToTail( pOpNode );
  122. }
  123. }
  124. //-----------------------------------------------------------------------------
  125. // CDependencyGraph destructor - releases temporary opNodes and attrNodes
  126. //-----------------------------------------------------------------------------
  127. CDependencyGraph::~CDependencyGraph()
  128. {
  129. Cleanup();
  130. }
  131. void CDependencyGraph::Cleanup()
  132. {
  133. VPROF_BUDGET( "CDependencyGraph::Cleanup", VPROF_BUDGETGROUP_TOOLS );
  134. int on = m_opNodes.Count();
  135. for ( int oi = 0; oi < on; ++oi )
  136. {
  137. g_OperatorNodePool.Free( m_opNodes[ oi ] );
  138. }
  139. UtlHashHandle_t h = m_attrNodes.GetFirstHandle();
  140. for ( ; h != m_attrNodes.InvalidHandle(); h = m_attrNodes.GetNextHandle( h ) )
  141. {
  142. g_AttrNodePool.Free( m_attrNodes[ h ] );
  143. }
  144. m_opRoots.RemoveAll();
  145. m_opNodes.RemoveAll();
  146. m_attrNodes.RemoveAll();
  147. m_operators.RemoveAll();
  148. }
  149. //-----------------------------------------------------------------------------
  150. // caches changed operators as roots - typically once per frame, every frame
  151. //-----------------------------------------------------------------------------
  152. void CDependencyGraph::FindRoots()
  153. {
  154. m_opRoots.RemoveAll();
  155. uint oi;
  156. uint on = m_opNodes.Count();
  157. for ( oi = 0; oi < on; ++oi )
  158. {
  159. COperatorNode *pOpNode = m_opNodes[ oi ];
  160. pOpNode->m_bInList = false;
  161. pOpNode->m_state = TS_NOT_VISITED;
  162. IDmeOperator *pOp = pOpNode->m_operator;
  163. if ( !pOp->IsDirty() )
  164. continue;
  165. m_opRoots.AddToTail( pOpNode );
  166. pOpNode->m_bInList = true;
  167. }
  168. // Do we have an attribute which is an input to us which is not an output to some other op?
  169. UtlHashHandle_t h = m_attrNodes.GetFirstHandle();
  170. for ( ; h != m_attrNodes.InvalidHandle(); h = m_attrNodes.GetNextHandle( h ) )
  171. {
  172. CAttributeNode *pAttrNode = m_attrNodes[ h ];
  173. //Msg( "attrib %s %p\n", pAttrNode->m_attribute->GetName(), pAttrNode->m_attribute );
  174. if ( !pAttrNode->m_bIsOutputToOperator &&
  175. pAttrNode->m_attribute->IsFlagSet( FATTRIB_OPERATOR_DIRTY ) )
  176. {
  177. on = pAttrNode->m_InputDependentOperators.Count();
  178. for ( oi = 0; oi < on; ++oi )
  179. {
  180. COperatorNode *pOpNode = pAttrNode->m_InputDependentOperators[ oi ];
  181. if ( !pOpNode->m_bInList )
  182. {
  183. m_opRoots.AddToTail( pOpNode );
  184. pOpNode->m_bInList = true;
  185. }
  186. }
  187. }
  188. pAttrNode->m_attribute->RemoveFlag( FATTRIB_OPERATOR_DIRTY );
  189. }
  190. }
  191. //-----------------------------------------------------------------------------
  192. // returns only the operators that need to be evaluated, sorted by dependencies
  193. //-----------------------------------------------------------------------------
  194. bool CDependencyGraph::CullAndSortOperators()
  195. {
  196. FindRoots();
  197. m_operators.RemoveAll();
  198. bool cycle = GetOperatorOrdering( m_opRoots, m_operators ); // leaves to roots (outputs to inputs)
  199. int on = m_operators.Count();
  200. int oh = on / 2;
  201. for ( int oi = 0; oi < oh; ++oi )
  202. {
  203. V_swap( m_operators[ oi ], m_operators[ on - oi - 1 ] );
  204. }
  205. return cycle;
  206. // return GetOperatorOrdering( m_opLeaves, operators ); // roots to leaves (inputs to outputs)
  207. }
  208. //-----------------------------------------------------------------------------
  209. // GetOperatorOrdering is just a recursive post-order depth-first-search
  210. // since we have two types of nodes, we manually traverse to the opnodes grandchildren, which are opnodes
  211. // (skipping children, which are attrnodes)
  212. // returns true if a cycle found - in this case, an arbitrary link of the cycle will be ignored
  213. //-----------------------------------------------------------------------------
  214. bool CDependencyGraph::GetOperatorOrdering( CUtlVector< COperatorNode * > &pOpNodes, CUtlVector< IDmeOperator * > &operators )
  215. {
  216. bool cycle = false;
  217. uint on = pOpNodes.Count();
  218. for ( uint oi = 0; oi < on; ++oi )
  219. {
  220. COperatorNode *pOpNode = pOpNodes[ oi ];
  221. if ( pOpNode->m_state != TS_NOT_VISITED )
  222. {
  223. if ( pOpNode->m_state == TS_VISITING )
  224. {
  225. cycle = true;
  226. }
  227. continue;
  228. }
  229. pOpNode->m_state = TS_VISITING; // mark as in being visited
  230. // DBG_PrintOperator( pIndent, pOpNode->m_operator );
  231. // leaves to roots (outputs to inputs)
  232. uint an = pOpNode->m_OutputAttributes.Count();
  233. for ( uint ai = 0; ai < an; ++ai )
  234. {
  235. CAttributeNode *pAttrNode = pOpNode->m_OutputAttributes[ ai ];
  236. if ( GetOperatorOrdering( pAttrNode->m_InputDependentOperators, operators ) )
  237. {
  238. cycle = true;
  239. }
  240. }
  241. operators.AddToTail( pOpNode->m_operator );
  242. pOpNode->m_state = TS_VISITED; // mark as done visiting
  243. }
  244. return cycle;
  245. }
  246. //-----------------------------------------------------------------------------
  247. // internal helper method - finds attrNode corresponding to pAttr
  248. //-----------------------------------------------------------------------------
  249. CAttributeNode *CDependencyGraph::FindAttrNode( CDmAttribute *pAttr )
  250. {
  251. VPROF_BUDGET( "CDependencyGraph::FindAttrNode", VPROF_BUDGETGROUP_TOOLS );
  252. Assert( pAttr );
  253. CAttributeNode search( pAttr );
  254. UtlHashHandle_t idx = m_attrNodes.Find( &search );
  255. if ( idx != m_attrNodes.InvalidHandle() )
  256. {
  257. return m_attrNodes.Element( idx );
  258. }
  259. CAttributeNode *pAttrNode = 0;
  260. {
  261. VPROF( "CDependencyGraph::FindAttrNode_Alloc" );
  262. pAttrNode = g_AttrNodePool.Alloc();
  263. pAttrNode->m_attribute = pAttr;
  264. }
  265. {
  266. VPROF( "CDependencyGraph::FindAttrNode_Alloc2" );
  267. m_attrNodes.Insert( pAttrNode );
  268. }
  269. return pAttrNode;
  270. }
  271. //-----------------------------------------------------------------------------
  272. // temporary internal debugging function
  273. //-----------------------------------------------------------------------------
  274. void CDependencyGraph::DBG_PrintOperator( const char *pIndent, IDmeOperator *pOp )
  275. {
  276. CDmElement *pElement = dynamic_cast< CDmElement* >( pOp );
  277. Msg( "%s%s <%s> {\n", pIndent, pElement->GetName(), pElement->GetTypeString() );
  278. }