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.

258 lines
9.0 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "pch_materialsystem.h"
  7. #define MATSYS_INTERNAL
  8. #include "occlusionquerymgr.h"
  9. #include "imaterialsysteminternal.h"
  10. #include "imatrendercontextinternal.h"
  11. // NOTE: This must be the last file included!!!
  12. #include "tier0/memdbgon.h"
  13. //-----------------------------------------------------------------------------
  14. // Singleton
  15. //-----------------------------------------------------------------------------
  16. static COcclusionQueryMgr s_OcclusionQueryMgr;
  17. COcclusionQueryMgr *g_pOcclusionQueryMgr = &s_OcclusionQueryMgr;
  18. //-----------------------------------------------------------------------------
  19. // Constructor
  20. //-----------------------------------------------------------------------------
  21. COcclusionQueryMgr::COcclusionQueryMgr()
  22. {
  23. m_nFrameCount = 0;
  24. }
  25. //-----------------------------------------------------------------------------
  26. // Allocate and delete query objects.
  27. //-----------------------------------------------------------------------------
  28. OcclusionQueryObjectHandle_t COcclusionQueryMgr::CreateOcclusionQueryObject( )
  29. {
  30. m_Mutex.Lock();
  31. int h = m_OcclusionQueryObjects.AddToTail();
  32. m_Mutex.Unlock();
  33. return (OcclusionQueryObjectHandle_t)h;
  34. }
  35. void COcclusionQueryMgr::OnCreateOcclusionQueryObject( OcclusionQueryObjectHandle_t h )
  36. {
  37. for ( int i = 0; i < COUNT_OCCLUSION_QUERY_STACK; i++)
  38. {
  39. m_OcclusionQueryObjects[(int)h].m_QueryHandle[i] = g_pShaderAPI->CreateOcclusionQueryObject( );
  40. }
  41. }
  42. // Flushes an outstanding query
  43. // HEY - Be very careful using this method - it causes a full pipeline flush/stall!
  44. void COcclusionQueryMgr::FlushQuery( OcclusionQueryObjectHandle_t hOcclusionQuery, int nIndex )
  45. {
  46. // Flush out any previous queries
  47. int h = (int)hOcclusionQuery;
  48. if ( m_OcclusionQueryObjects[h].m_bHasBeenIssued[nIndex] )
  49. {
  50. ShaderAPIOcclusionQuery_t hQuery = m_OcclusionQueryObjects[h].m_QueryHandle[nIndex];
  51. while ( OCCLUSION_QUERY_RESULT_PENDING == g_pShaderAPI->OcclusionQuery_GetNumPixelsRendered( hQuery, true ) )
  52. continue;
  53. }
  54. }
  55. void COcclusionQueryMgr::DestroyOcclusionQueryObject( OcclusionQueryObjectHandle_t hOcclusionQuery )
  56. {
  57. int h = (int)hOcclusionQuery;
  58. Assert( m_OcclusionQueryObjects.IsValidIndex( h ) );
  59. if ( m_OcclusionQueryObjects.IsValidIndex( h ) )
  60. {
  61. for ( int i = 0; i < COUNT_OCCLUSION_QUERY_STACK; i++)
  62. {
  63. if ( m_OcclusionQueryObjects[h].m_QueryHandle[i] != INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE )
  64. {
  65. g_pShaderAPI->DestroyOcclusionQueryObject( m_OcclusionQueryObjects[h].m_QueryHandle[i] );
  66. }
  67. }
  68. m_Mutex.Lock();
  69. m_OcclusionQueryObjects.Remove( h );
  70. m_Mutex.Unlock();
  71. }
  72. }
  73. //-----------------------------------------------------------------------------
  74. // Advance frame
  75. //-----------------------------------------------------------------------------
  76. void COcclusionQueryMgr::AdvanceFrame()
  77. {
  78. ++m_nFrameCount;
  79. }
  80. //-----------------------------------------------------------------------------
  81. // Alt-tab support
  82. // NOTE: This doesn't queue anything up
  83. //-----------------------------------------------------------------------------
  84. void COcclusionQueryMgr::AllocOcclusionQueryObjects( void )
  85. {
  86. FOR_EACH_LL( m_OcclusionQueryObjects, iterator )
  87. {
  88. for ( int i = 0; i < COUNT_OCCLUSION_QUERY_STACK; i++)
  89. {
  90. m_OcclusionQueryObjects[iterator].m_QueryHandle[i] = g_pShaderAPI->CreateOcclusionQueryObject();
  91. m_OcclusionQueryObjects[iterator].m_bHasBeenIssued[i] = false; // any in-flight queries are never returning
  92. }
  93. }
  94. }
  95. void COcclusionQueryMgr::FreeOcclusionQueryObjects( void )
  96. {
  97. FOR_EACH_LL( m_OcclusionQueryObjects, iterator )
  98. {
  99. for ( int i = 0; i < COUNT_OCCLUSION_QUERY_STACK; i++)
  100. {
  101. if ( m_OcclusionQueryObjects[iterator].m_QueryHandle[i] != INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE )
  102. {
  103. g_pShaderAPI->DestroyOcclusionQueryObject( m_OcclusionQueryObjects[iterator].m_QueryHandle[i] );
  104. m_OcclusionQueryObjects[iterator].m_QueryHandle[i] = INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE;
  105. m_OcclusionQueryObjects[iterator].m_bHasBeenIssued[i] = false;
  106. }
  107. }
  108. }
  109. }
  110. //-----------------------------------------------------------------------------
  111. // Used to make the handle think it's never had a successful query before
  112. //-----------------------------------------------------------------------------
  113. void COcclusionQueryMgr::ResetOcclusionQueryObject( OcclusionQueryObjectHandle_t hOcclusionQuery )
  114. {
  115. int h = (int)hOcclusionQuery;
  116. Assert( m_OcclusionQueryObjects.IsValidIndex( h ) );
  117. if ( m_OcclusionQueryObjects.IsValidIndex( h ) )
  118. {
  119. // Forget we've issued any previous queries - there's no need to flush them.
  120. for ( int i = 0; i < COUNT_OCCLUSION_QUERY_STACK; i++)
  121. {
  122. m_OcclusionQueryObjects[h].m_bHasBeenIssued[i] = false;
  123. }
  124. m_OcclusionQueryObjects[h].m_LastResult = -1;
  125. m_OcclusionQueryObjects[h].m_nFrameIssued = -1;
  126. }
  127. }
  128. //-----------------------------------------------------------------------------
  129. // Bracket drawing with begin and end so that we can get counts next frame.
  130. //-----------------------------------------------------------------------------
  131. void COcclusionQueryMgr::BeginOcclusionQueryDrawing( OcclusionQueryObjectHandle_t hOcclusionQuery )
  132. {
  133. int h = (int)hOcclusionQuery;
  134. Assert( m_OcclusionQueryObjects.IsValidIndex( h ) );
  135. if ( m_OcclusionQueryObjects.IsValidIndex( h ) )
  136. {
  137. int nCurrent = m_OcclusionQueryObjects[h].m_nCurrentIssue;
  138. ShaderAPIOcclusionQuery_t hQuery = m_OcclusionQueryObjects[h].m_QueryHandle[nCurrent];
  139. if ( hQuery != INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE )
  140. {
  141. // If it's been issued, but we haven't gotten a result when we polled last time,
  142. // try polling one last time, since we can't poll again after we issue again.
  143. if ( m_OcclusionQueryObjects[h].m_bHasBeenIssued[nCurrent] )
  144. {
  145. int nPixels = g_pShaderAPI->OcclusionQuery_GetNumPixelsRendered( hQuery, false );
  146. if ( ( nPixels == OCCLUSION_QUERY_RESULT_PENDING ) && ( m_OcclusionQueryObjects[h].m_nFrameIssued == m_nFrameCount ) )
  147. {
  148. static int s_nWarnCount = 0;
  149. if ( s_nWarnCount++ < 5 )
  150. {
  151. DevWarning( "blocking issue in occlusion queries! Grab brian!\n" );
  152. }
  153. }
  154. while( !OCCLUSION_QUERY_FINISHED( nPixels ) )
  155. {
  156. // We're going to reuse this query, so issue a flush to force the query results to come back.
  157. nPixels = g_pShaderAPI->OcclusionQuery_GetNumPixelsRendered( hQuery, true );
  158. }
  159. if ( nPixels >= 0 )
  160. {
  161. m_OcclusionQueryObjects[h].m_LastResult = nPixels;
  162. }
  163. m_OcclusionQueryObjects[h].m_bHasBeenIssued[nCurrent] = false;
  164. }
  165. g_pShaderAPI->BeginOcclusionQueryDrawing( hQuery );
  166. }
  167. }
  168. }
  169. void COcclusionQueryMgr::EndOcclusionQueryDrawing( OcclusionQueryObjectHandle_t hOcclusionQuery )
  170. {
  171. int h = (int)hOcclusionQuery;
  172. Assert( m_OcclusionQueryObjects.IsValidIndex( h ) );
  173. if ( m_OcclusionQueryObjects.IsValidIndex( h ) )
  174. {
  175. int nCurrent = m_OcclusionQueryObjects[h].m_nCurrentIssue;
  176. ShaderAPIOcclusionQuery_t hQuery = m_OcclusionQueryObjects[h].m_QueryHandle[nCurrent];
  177. if ( hQuery != INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE )
  178. {
  179. g_pShaderAPI->EndOcclusionQueryDrawing( hQuery );
  180. m_OcclusionQueryObjects[h].m_bHasBeenIssued[nCurrent] = true;
  181. m_OcclusionQueryObjects[h].m_nFrameIssued = m_nFrameCount;
  182. nCurrent = ( nCurrent + 1 ) % COUNT_OCCLUSION_QUERY_STACK;
  183. m_OcclusionQueryObjects[h].m_nCurrentIssue = nCurrent;
  184. }
  185. }
  186. }
  187. //-----------------------------------------------------------------------------
  188. // Get the number of pixels rendered between begin and end on an earlier frame.
  189. // Calling this in the same frame is a huge perf hit!
  190. //-----------------------------------------------------------------------------
  191. void COcclusionQueryMgr::OcclusionQuery_IssueNumPixelsRenderedQuery( OcclusionQueryObjectHandle_t hOcclusionQuery )
  192. {
  193. int h = (int)hOcclusionQuery;
  194. Assert( m_OcclusionQueryObjects.IsValidIndex( h ) );
  195. if ( m_OcclusionQueryObjects.IsValidIndex( h ) )
  196. {
  197. for( int i = 0; i < COUNT_OCCLUSION_QUERY_STACK; i++ )
  198. {
  199. int nIndex = ( m_OcclusionQueryObjects[h].m_nCurrentIssue + i ) % COUNT_OCCLUSION_QUERY_STACK;
  200. ShaderAPIOcclusionQuery_t hQuery = m_OcclusionQueryObjects[h].m_QueryHandle[nIndex];
  201. if ( hQuery != INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE && m_OcclusionQueryObjects[h].m_bHasBeenIssued[nIndex] )
  202. {
  203. int nPixels = g_pShaderAPI->OcclusionQuery_GetNumPixelsRendered( hQuery );
  204. if ( nPixels == OCCLUSION_QUERY_RESULT_ERROR )
  205. {
  206. // In GL mode, it's possible for queries to fail (say when mat_queue_mode is toggled). In this case, just clear m_bHasBeenIssued and forget we ever issued this query.
  207. m_OcclusionQueryObjects[h].m_bHasBeenIssued[nIndex] = false;
  208. }
  209. else if ( nPixels >= 0 )
  210. {
  211. m_OcclusionQueryObjects[h].m_LastResult = nPixels;
  212. m_OcclusionQueryObjects[h].m_bHasBeenIssued[nIndex] = false;
  213. }
  214. }
  215. }
  216. }
  217. }
  218. int COcclusionQueryMgr::OcclusionQuery_GetNumPixelsRendered( OcclusionQueryObjectHandle_t h, bool bDoQuery )
  219. {
  220. if ( bDoQuery )
  221. {
  222. OcclusionQuery_IssueNumPixelsRenderedQuery( h );
  223. }
  224. int nPixels = m_OcclusionQueryObjects[(int)h].m_LastResult;
  225. return nPixels;
  226. }