Counter Strike : Global Offensive Source Code
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.

341 lines
7.7 KiB

  1. //============ Copyright (c) Valve Corporation, All rights reserved. ============
  2. //
  3. // cglmquery.cpp
  4. //
  5. //===============================================================================
  6. #include "togl/rendermechanism.h"
  7. #ifndef _WIN32
  8. #include <unistd.h>
  9. #endif
  10. // memdbgon -must- be the last include file in a .cpp file.
  11. #include "tier0/memdbgon.h"
  12. //===============================================================================
  13. // http://www.opengl.org/registry/specs/ARB/occlusion_query.txt
  14. // Workaround for "Calling either GenQueriesARB or DeleteQueriesARB while any query of any target is active causes an INVALID_OPERATION error to be generated."
  15. uint CGLMQuery::s_nTotalOcclusionQueryCreatesOrDeletes;
  16. extern ConVar gl_errorcheckall;
  17. extern ConVar gl_errorcheckqueries;
  18. extern ConVar gl_errorchecknone;
  19. // how many microseconds to wait after a failed query-available test
  20. // presently on MTGL this doesn't happen, but it could change, keep this handy
  21. ConVar gl_nullqueries( "gl_nullqueries", "0" );
  22. //===============================================================================
  23. CGLMQuery::CGLMQuery( GLMContext *ctx, GLMQueryParams *params )
  24. {
  25. // get the type of query requested
  26. // generate name(s) needed
  27. // set initial state appropriately
  28. m_ctx = ctx;
  29. m_params = *params;
  30. m_name = 0;
  31. m_syncobj = 0;
  32. m_started = m_stopped = m_done = false;
  33. m_nullQuery = false;
  34. // assume value of convar at start time
  35. // does not change during individual query lifetime
  36. // started null = stays null
  37. // started live = stays live
  38. switch(m_params.m_type)
  39. {
  40. case EOcclusion:
  41. {
  42. //make an occlusion query (and a fence to go with it)
  43. gGL->glGenQueriesARB( 1, &m_name );
  44. s_nTotalOcclusionQueryCreatesOrDeletes++;
  45. GLMPRINTF(("-A- CGLMQuery(OQ) created name %d", m_name));
  46. }
  47. break;
  48. case EFence:
  49. //make a fence - no aux fence needed
  50. m_syncobj = 0;
  51. if (gGL->m_bHave_GL_ARB_sync)
  52. { /* GL_ARB_sync doesn't separate gen and set, so we do glFenceSync() later. */ }
  53. else if (gGL->m_bHave_GL_NV_fence)
  54. gGL->glGenFencesNV(1, &m_name );
  55. else if (gGL->m_bHave_GL_APPLE_fence)
  56. gGL->glGenFencesAPPLE(1, &m_name );
  57. GLMPRINTF(("-A- CGLMQuery(fence) created name %d", m_name));
  58. break;
  59. }
  60. }
  61. CGLMQuery::~CGLMQuery()
  62. {
  63. GLMPRINTF(("-A-> ~CGLMQuery"));
  64. // make sure query has completed (might not be necessary)
  65. // delete the name(s)
  66. switch(m_params.m_type)
  67. {
  68. case EOcclusion:
  69. {
  70. // do a finish occlusion query ?
  71. GLMPRINTF(("-A- ~CGLMQuery(OQ) deleting name %d", m_name));
  72. gGL->glDeleteQueriesARB(1, &m_name );
  73. s_nTotalOcclusionQueryCreatesOrDeletes++;
  74. }
  75. break;
  76. case EFence:
  77. {
  78. // do a finish fence ?
  79. GLMPRINTF(("-A- ~CGLMQuery(fence) deleting name %llu", gGL->m_bHave_GL_ARB_sync ? (unsigned long long) m_syncobj : (unsigned long long) m_name));
  80. #ifdef HAVE_GL_ARB_SYNC
  81. if (gGL->m_bHave_GL_ARB_sync)
  82. gGL->glDeleteSync( m_syncobj );
  83. else
  84. #endif
  85. if (gGL->m_bHave_GL_NV_fence)
  86. gGL->glDeleteFencesNV(1, &m_name );
  87. else if (gGL->m_bHave_GL_APPLE_fence)
  88. gGL->glDeleteFencesAPPLE(1, &m_name );
  89. }
  90. break;
  91. }
  92. m_name = 0;
  93. m_syncobj = 0;
  94. GLMPRINTF(("-A-< ~CGLMQuery"));
  95. }
  96. void CGLMQuery::Start( void ) // "start counting"
  97. {
  98. m_nullQuery = (gl_nullqueries.GetInt() != 0); // latch value for remainder of query life
  99. m_started = true;
  100. m_stopped = false;
  101. m_done = false;
  102. switch(m_params.m_type)
  103. {
  104. case EOcclusion:
  105. {
  106. if (m_nullQuery)
  107. {
  108. // do nothing..
  109. }
  110. else
  111. {
  112. gGL->glBeginQueryARB( GL_SAMPLES_PASSED_ARB, m_name );
  113. }
  114. }
  115. break;
  116. case EFence:
  117. #ifdef HAVE_GL_ARB_SYNC
  118. if (gGL->m_bHave_GL_ARB_sync)
  119. {
  120. if (m_syncobj != 0) gGL->glDeleteSync(m_syncobj);
  121. m_syncobj = gGL->glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
  122. }
  123. else
  124. #endif
  125. if (gGL->m_bHave_GL_NV_fence)
  126. gGL->glSetFenceNV( m_name, GL_ALL_COMPLETED_NV );
  127. else if (gGL->m_bHave_GL_APPLE_fence)
  128. gGL->glSetFenceAPPLE( m_name );
  129. m_stopped = true; // caller should not call Stop on a fence, it self-stops
  130. break;
  131. }
  132. }
  133. void CGLMQuery::Stop( void ) // "stop counting"
  134. {
  135. Assert(m_started);
  136. if ( m_stopped )
  137. return;
  138. switch(m_params.m_type)
  139. {
  140. case EOcclusion:
  141. {
  142. if (m_nullQuery)
  143. {
  144. // do nothing..
  145. }
  146. else
  147. {
  148. gGL->glEndQueryARB( GL_SAMPLES_PASSED_ARB ); // we are only putting the request-to-stop-counting into the cmd stream.
  149. }
  150. }
  151. break;
  152. case EFence:
  153. // nop - you don't "end" a fence, you just test it and/or finish it out in Complete
  154. break;
  155. }
  156. m_stopped = true;
  157. }
  158. bool CGLMQuery::IsDone( void )
  159. {
  160. Assert(m_started);
  161. Assert(m_stopped);
  162. if(!m_done) // you can ask more than once, but we only check until it comes back as done.
  163. {
  164. // on occlusion: glGetQueryObjectivARB - large cost on pre SLGU, cheap after
  165. // on fence: glTestFence* on the fence
  166. switch(m_params.m_type)
  167. {
  168. case EOcclusion: // just test the fence that was set after the query begin
  169. {
  170. if (m_nullQuery)
  171. {
  172. // do almost nothing.. but claim work is complete
  173. m_done = true;
  174. }
  175. else
  176. {
  177. // prepare to pay a big price on drivers prior to 10.6.4+SLGU
  178. GLint available = 0;
  179. gGL->glGetQueryObjectivARB(m_name, GL_QUERY_RESULT_AVAILABLE_ARB, &available );
  180. m_done = (available != 0);
  181. }
  182. }
  183. break;
  184. case EFence:
  185. {
  186. #ifdef HAVE_GL_ARB_SYNC
  187. if (gGL->m_bHave_GL_ARB_sync)
  188. m_done = (gGL->glClientWaitSync( m_syncobj, 0, 0 ) == GL_ALREADY_SIGNALED);
  189. else
  190. #endif
  191. if ( m_name == 0 )
  192. m_done = true;
  193. else if (gGL->m_bHave_GL_NV_fence)
  194. m_done = gGL->glTestFenceNV( m_name ) != 0;
  195. else if (gGL->m_bHave_GL_APPLE_fence)
  196. m_done = gGL->glTestFenceAPPLE( m_name ) != 0;
  197. if (m_done)
  198. {
  199. if (gGL->m_bHave_GL_ARB_sync)
  200. { /* no-op; we already know it's set to GL_ALREADY_SIGNALED. */ }
  201. else
  202. {
  203. if (gGL->m_bHave_GL_NV_fence)
  204. gGL->glFinishFenceNV( m_name ); // no set fence goes un-finished
  205. else if (gGL->m_bHave_GL_APPLE_fence)
  206. gGL->glFinishFenceAPPLE( m_name ); // no set fence goes un-finished
  207. }
  208. }
  209. }
  210. break;
  211. }
  212. }
  213. return m_done;
  214. }
  215. void CGLMQuery::Complete( uint *result )
  216. {
  217. uint resultval = 0;
  218. //bool bogus_available = false;
  219. // blocking call if not done
  220. Assert(m_started);
  221. Assert(m_stopped);
  222. switch(m_params.m_type)
  223. {
  224. case EOcclusion:
  225. {
  226. if (m_nullQuery)
  227. {
  228. m_done = true;
  229. resultval = 0; // we did say "null queries..."
  230. }
  231. else
  232. {
  233. gGL->glGetQueryObjectuivARB( m_name, GL_QUERY_RESULT_ARB, &resultval);
  234. m_done = true;
  235. }
  236. }
  237. break;
  238. case EFence:
  239. {
  240. if(!m_done)
  241. {
  242. #ifdef HAVE_GL_ARB_SYNC
  243. if (gGL->m_bHave_GL_ARB_sync)
  244. {
  245. if (gGL->glClientWaitSync( m_syncobj, 0, 0 ) != GL_ALREADY_SIGNALED)
  246. {
  247. GLenum syncstate;
  248. do {
  249. const GLuint64 timeout = 10 * ((GLuint64)1000 * 1000 * 1000); // 10 seconds in nanoseconds.
  250. (void)timeout;
  251. syncstate = gGL->glClientWaitSync( m_syncobj, GL_SYNC_FLUSH_COMMANDS_BIT, 0 );
  252. } while (syncstate == GL_TIMEOUT_EXPIRED); // any errors or success break out of this loop.
  253. }
  254. }
  255. else
  256. #endif
  257. if (gGL->m_bHave_GL_NV_fence)
  258. gGL->glFinishFenceNV( m_name );
  259. else if (gGL->m_bHave_GL_APPLE_fence)
  260. gGL->glFinishFenceAPPLE( m_name );
  261. m_done = true; // for clarity or if they try to Complete twice
  262. }
  263. }
  264. break;
  265. }
  266. Assert( m_done );
  267. // reset state for re-use - i.e. you have to call Complete if you want to re-use the object
  268. m_started = m_stopped = m_done = false;
  269. if (result) // caller may pass NULL if not interested in result, for example to clear a fence
  270. {
  271. *result = resultval;
  272. }
  273. }
  274. // accessors for the started/stopped state
  275. bool CGLMQuery::IsStarted ( void )
  276. {
  277. return m_started;
  278. }
  279. bool CGLMQuery::IsStopped ( void )
  280. {
  281. return m_stopped;
  282. }