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.

363 lines
8.9 KiB

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