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.

1161 lines
35 KiB

  1. //===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #include "cbase.h"
  9. #include "c_pixel_visibility.h"
  10. #include "materialsystem/imesh.h"
  11. #include "materialsystem/imaterial.h"
  12. #include "precache_register.h"
  13. #include "view.h"
  14. #include "viewrender.h"
  15. #include "utlmultilist.h"
  16. #include "vprof.h"
  17. // NOTE: This has to be the last file included!
  18. #include "tier0/memdbgon.h"
  19. static void PixelvisDrawChanged( IConVar *pPixelvisVar, const char *pOld, float flOldValue );
  20. ConVar r_pixelvisibility_partial( "r_pixelvisibility_partial", "1" );
  21. //#if defined( DX_TO_GL_ABSTRACTION )
  22. //ConVar r_dopixelvisibility( "r_dopixelvisibility", "1" );
  23. //#else
  24. ConVar r_dopixelvisibility( "r_dopixelvisibility", "1" );
  25. ConVar r_drawpixelvisibility( "r_drawpixelvisibility", "0", 0, "Show the occlusion proxies", PixelvisDrawChanged );
  26. ConVar r_pixelvisibility_spew( "r_pixelvisibility_spew", "0" );
  27. #ifdef OSX
  28. // GLMgr will set this one to "1" if it senses the new post-10.6.4 driver (m_hasPerfPackage1)
  29. ConVar gl_can_query_fast( "gl_can_query_fast", "0" );
  30. static bool HasFastQueries( void )
  31. {
  32. return gl_can_query_fast.GetBool();
  33. }
  34. #else
  35. // non OSX path
  36. static bool HasFastQueries( void )
  37. {
  38. return true;
  39. }
  40. #endif
  41. extern ConVar building_cubemaps;
  42. #ifndef _GAMECONSOLE
  43. const float MIN_PROXY_PIXELS = 5.0f;
  44. #else
  45. const float MIN_PROXY_PIXELS = 25.0f;
  46. #endif
  47. extern view_id_t CurrentViewID();
  48. struct OcclusionHandleViewIDPair_t
  49. {
  50. OcclusionQueryObjectHandle_t hOcclusionHandle;
  51. int iViewID;
  52. int iLastFrameRendered;
  53. };
  54. struct OcclusionQueryHiddenData_t
  55. {
  56. COcclusionQuerySet *pOwner;
  57. CUtlVector<OcclusionHandleViewIDPair_t> occlusionHandles[MAX_SPLITSCREEN_PLAYERS];
  58. };
  59. static CUtlVector<OcclusionQueryHiddenData_t> s_OcclusionQueries;
  60. static inline int FindQueryHandlePairIndex( OcclusionQueryHiddenData_t *pData, int iViewID, int iSplitScreenSlot )
  61. {
  62. int iPairCount = pData->occlusionHandles[iSplitScreenSlot].Count();
  63. OcclusionHandleViewIDPair_t *pPairs = pData->occlusionHandles[iSplitScreenSlot].Base();
  64. for( int i = 0; i != iPairCount; ++i )
  65. {
  66. if( pPairs[i].iViewID == iViewID )
  67. return i;
  68. }
  69. return pData->occlusionHandles[iSplitScreenSlot].InvalidIndex();
  70. }
  71. float PixelVisibility_DrawProxy( IMatRenderContext *pRenderContext, OcclusionQueryObjectHandle_t queryHandle, Vector origin, float scale, float proxyAspect, IMaterial *pMaterial, bool screenspace )
  72. {
  73. Vector point;
  74. // don't expand this with distance to fit pixels or the sprite will poke through
  75. // only expand the parts perpendicular to the view
  76. float forwardScale = scale;
  77. // draw a pyramid of points touching a sphere of radius "scale" at origin
  78. float pixelsPerUnit = pRenderContext->ComputePixelDiameterOfSphere( origin, 1.0f );
  79. pixelsPerUnit = MAX( pixelsPerUnit, 1e-4f );
  80. if ( screenspace )
  81. {
  82. // Force this to be the size of a sphere of diameter "scale" at some reference distance (1.0 unit)
  83. float pixelsPerUnit2 = pRenderContext->ComputePixelDiameterOfSphere( CurrentViewOrigin() + CurrentViewForward()*1.0f, scale*0.5f );
  84. // force drawing of "scale" pixels
  85. scale = pixelsPerUnit2 / pixelsPerUnit;
  86. }
  87. else
  88. {
  89. float pixels = scale * pixelsPerUnit;
  90. // make the radius larger to ensure a minimum screen space size of the proxy geometry
  91. if ( pixels < MIN_PROXY_PIXELS )
  92. {
  93. scale = MIN_PROXY_PIXELS / pixelsPerUnit;
  94. }
  95. }
  96. // collapses the pyramid to a plane - so this could be a quad instead
  97. Vector dir = origin - CurrentViewOrigin();
  98. VectorNormalize(dir);
  99. origin -= dir * forwardScale;
  100. forwardScale = 0.0f;
  101. //
  102. Vector verts[5];
  103. const float sqrt2 = 0.707106781f; // sqrt(2) - keeps all vectors the same length from origin
  104. scale *= sqrt2;
  105. float scale45x = scale;
  106. float scale45y = scale / proxyAspect;
  107. verts[0] = origin - CurrentViewForward() * forwardScale; // the apex of the pyramid
  108. verts[1] = origin + CurrentViewUp() * scale45y - CurrentViewRight() * scale45x; // these four form the base
  109. verts[2] = origin + CurrentViewUp() * scale45y + CurrentViewRight() * scale45x; // the pyramid is a sprite with a point that
  110. verts[3] = origin - CurrentViewUp() * scale45y + CurrentViewRight() * scale45x; // pokes back toward the camera through any nearby
  111. verts[4] = origin - CurrentViewUp() * scale45y - CurrentViewRight() * scale45x; // geometry
  112. // get screen coords of edges
  113. Vector screen[4];
  114. for ( int i = 0; i < 4; i++ )
  115. {
  116. extern int ScreenTransform( const Vector& point, Vector& screen );
  117. if ( ScreenTransform( verts[i+1], screen[i] ) )
  118. return -1;
  119. }
  120. // compute area and screen-clipped area
  121. float w = screen[1].x - screen[0].x;
  122. float h = screen[0].y - screen[3].y;
  123. float ws = MIN(1.0f, screen[1].x) - MAX(-1.0f, screen[0].x);
  124. float hs = MIN(1.0f, screen[0].y) - MAX(-1.0f, screen[3].y);
  125. float area = w*h; // area can be zero when we ALT-TAB
  126. float areaClipped = ws*hs;
  127. float ratio = 0.0f;
  128. if ( area != 0 )
  129. {
  130. // compute the ratio of the area not clipped by the frustum to total area
  131. ratio = areaClipped / area;
  132. ratio = clamp(ratio, 0.0f, 1.0f);
  133. }
  134. pRenderContext->BeginOcclusionQueryDrawing( queryHandle );
  135. CMeshBuilder meshBuilder;
  136. IMesh* pMesh = pRenderContext->GetDynamicMesh( false, NULL, NULL, pMaterial );
  137. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, 4 );
  138. // draw a pyramid
  139. for ( int i = 0; i < 4; i++ )
  140. {
  141. int a = i+1;
  142. int b = (a%4)+1;
  143. meshBuilder.Position3fv( verts[0].Base() );
  144. meshBuilder.AdvanceVertex();
  145. meshBuilder.Position3fv( verts[a].Base() );
  146. meshBuilder.AdvanceVertex();
  147. meshBuilder.Position3fv( verts[b].Base() );
  148. meshBuilder.AdvanceVertex();
  149. }
  150. meshBuilder.End();
  151. pMesh->Draw();
  152. // sprite/quad proxy
  153. #if 0
  154. meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
  155. VectorMA (origin, -scale, CurrentViewUp(), point);
  156. VectorMA (point, -scale, CurrentViewRight(), point);
  157. meshBuilder.Position3fv (point.Base());
  158. meshBuilder.AdvanceVertex();
  159. VectorMA (origin, scale, CurrentViewUp(), point);
  160. VectorMA (point, -scale, CurrentViewRight(), point);
  161. meshBuilder.Position3fv (point.Base());
  162. meshBuilder.AdvanceVertex();
  163. VectorMA (origin, scale, CurrentViewUp(), point);
  164. VectorMA (point, scale, CurrentViewRight(), point);
  165. meshBuilder.Position3fv (point.Base());
  166. meshBuilder.AdvanceVertex();
  167. VectorMA (origin, -scale, CurrentViewUp(), point);
  168. VectorMA (point, scale, CurrentViewRight(), point);
  169. meshBuilder.Position3fv (point.Base());
  170. meshBuilder.AdvanceVertex();
  171. meshBuilder.End();
  172. pMesh->Draw();
  173. #endif
  174. pRenderContext->EndOcclusionQueryDrawing( queryHandle );
  175. // fraction clipped by frustum
  176. return ratio;
  177. }
  178. class CPixelVisSet
  179. {
  180. public:
  181. void Init( const pixelvis_queryparams_t &params );
  182. void MarkActive();
  183. bool IsActive();
  184. CPixelVisSet()
  185. {
  186. frameIssued = 0;
  187. serial = 0;
  188. queryList = 0xFFFF;
  189. sizeIsScreenSpace = false;
  190. }
  191. public:
  192. float proxySize;
  193. float proxyAspect;
  194. float fadeTimeInv;
  195. unsigned short queryList;
  196. unsigned short serial;
  197. bool sizeIsScreenSpace;
  198. private:
  199. int frameIssued;
  200. };
  201. void CPixelVisSet::Init( const pixelvis_queryparams_t &params )
  202. {
  203. Assert( params.bSetup );
  204. proxySize = params.proxySize;
  205. proxyAspect = params.proxyAspect;
  206. if ( params.fadeTime > 0.0f )
  207. {
  208. fadeTimeInv = 1.0f / params.fadeTime;
  209. }
  210. else
  211. {
  212. // fade in over 0.125 seconds
  213. fadeTimeInv = 1.0f / 0.125f;
  214. }
  215. frameIssued = 0;
  216. sizeIsScreenSpace = params.bSizeInScreenspace;
  217. }
  218. void CPixelVisSet::MarkActive()
  219. {
  220. frameIssued = gpGlobals->framecount;
  221. }
  222. bool CPixelVisSet::IsActive()
  223. {
  224. return (gpGlobals->framecount - frameIssued) > 1 ? false : true;
  225. }
  226. class CPixelVisibilityQuery
  227. {
  228. public:
  229. CPixelVisibilityQuery();
  230. ~CPixelVisibilityQuery();
  231. bool IsValid();
  232. bool IsForView( int nPlayerSlot, int viewID );
  233. bool IsActive();
  234. float GetFractionVisible( float fadeTimeInv );
  235. void IssueQuery( IMatRenderContext *pRenderContext, float proxySize, float proxyAspect, IMaterial *pMaterial, bool sizeIsScreenSpace );
  236. void IssueCountingQuery( IMatRenderContext *pRenderContext, float proxySize, float proxyAspect, IMaterial *pMaterial, bool sizeIsScreenSpace );
  237. void ResetOcclusionQueries();
  238. void SetView( int nPlayerSlot, int viewID )
  239. {
  240. // This is necessary since player slot is stored in 2 bits
  241. COMPILE_TIME_ASSERT( MAX_SPLITSCREEN_PLAYERS <= 4 );
  242. Assert( nPlayerSlot >= 0 && nPlayerSlot < MAX_SPLITSCREEN_PLAYERS );
  243. m_viewID = viewID;
  244. m_brightnessTarget = 0.0f;
  245. m_clipFraction = 1.0f;
  246. m_frameIssued = -1;
  247. m_failed = false;
  248. m_wasQueriedThisFrame = false;
  249. m_hasValidQueryResults = false;
  250. m_nPlayerSlot = nPlayerSlot;
  251. }
  252. public:
  253. Vector m_origin;
  254. int m_frameIssued;
  255. private:
  256. float m_brightnessTarget;
  257. float m_clipFraction;
  258. OcclusionQueryObjectHandle_t m_queryHandle;
  259. OcclusionQueryObjectHandle_t m_queryHandleCount;
  260. unsigned short m_wasQueriedThisFrame : 1;
  261. unsigned short m_failed : 1;
  262. unsigned short m_hasValidQueryResults : 1;
  263. unsigned short m_nPlayerSlot : 2;
  264. unsigned short m_pad : 11;
  265. unsigned short m_viewID;
  266. friend void PixelVisibility_ShiftVisibilityViews( int nPlayerSlot, int iSourceViewID, int iDestViewID ); //need direct access to private data to make shifting smooth
  267. };
  268. CPixelVisibilityQuery::CPixelVisibilityQuery()
  269. {
  270. CMatRenderContextPtr pRenderContext( materials );
  271. SetView( 0, 0xFFFF );
  272. m_queryHandle = pRenderContext->CreateOcclusionQueryObject();
  273. m_queryHandleCount = pRenderContext->CreateOcclusionQueryObject();
  274. }
  275. CPixelVisibilityQuery::~CPixelVisibilityQuery()
  276. {
  277. CMatRenderContextPtr pRenderContext( materials );
  278. if ( m_queryHandle != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE )
  279. {
  280. pRenderContext->DestroyOcclusionQueryObject( m_queryHandle );
  281. }
  282. if ( m_queryHandleCount != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE )
  283. {
  284. pRenderContext->DestroyOcclusionQueryObject( m_queryHandleCount );
  285. }
  286. }
  287. void CPixelVisibilityQuery::ResetOcclusionQueries()
  288. {
  289. // NOTE: Since we're keeping the CPixelVisibilityQuery objects around in a pool
  290. // and not actually deleting them, this means that our material system occlusion queries are
  291. // not being deleted either. Which means that if a CPixelVisibilityQuery is
  292. // put into the free list and then immediately re-used, then we have an opportunity for
  293. // a bug: What can happen on the first frame of the material system query
  294. // is that if the query isn't done yet, it will use the last queried value
  295. // which will happen to be set to the value of the last query done
  296. // for the previous CPixelVisSet the CPixelVisibilityQuery happened to be associated with
  297. // which makes queries have an invalid value for the first frame
  298. // This will mark the occlusion query objects as not ever having been read from before
  299. CMatRenderContextPtr pRenderContext( materials );
  300. if ( m_queryHandle != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE )
  301. {
  302. pRenderContext->ResetOcclusionQueryObject( m_queryHandle );
  303. }
  304. if ( m_queryHandleCount != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE )
  305. {
  306. pRenderContext->ResetOcclusionQueryObject( m_queryHandleCount );
  307. }
  308. }
  309. bool CPixelVisibilityQuery::IsValid()
  310. {
  311. return (m_queryHandle != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE) ? true : false;
  312. }
  313. bool CPixelVisibilityQuery::IsForView( int nPlayerSlot, int viewID )
  314. {
  315. return ( m_viewID == viewID ) && ( nPlayerSlot == m_nPlayerSlot );
  316. }
  317. bool CPixelVisibilityQuery::IsActive()
  318. {
  319. return ( gpGlobals->framecount - m_frameIssued ) > 1 ? false : true;
  320. }
  321. float CPixelVisibilityQuery::GetFractionVisible( float fadeTimeInv )
  322. {
  323. if ( !IsValid() )
  324. return 0.0f;
  325. if ( !m_wasQueriedThisFrame )
  326. {
  327. CMatRenderContextPtr pRenderContext( materials );
  328. m_wasQueriedThisFrame = true;
  329. int pixels = -1;
  330. int pixelsPossible = -1;
  331. if ( r_pixelvisibility_partial.GetBool() )
  332. {
  333. if ( m_frameIssued != -1 )
  334. {
  335. pixelsPossible = pRenderContext->OcclusionQuery_GetNumPixelsRendered( m_queryHandleCount );
  336. pixels = pRenderContext->OcclusionQuery_GetNumPixelsRendered( m_queryHandle );
  337. }
  338. if ( r_pixelvisibility_spew.GetBool() && CurrentViewID() == 0 )
  339. {
  340. DevMsg( 1, "Pixels visible: %d (qh:%p) Pixels possible: %d (qh:%p) (frame:%d)\n", pixels, m_queryHandle, pixelsPossible, m_queryHandleCount, gpGlobals->framecount );
  341. }
  342. if ( pixels < 0 || pixelsPossible < 0 )
  343. {
  344. m_failed = ( m_frameIssued >= 0 ) ? true : false;
  345. return m_brightnessTarget * m_clipFraction;
  346. }
  347. m_hasValidQueryResults = true;
  348. if ( pixelsPossible > 0 )
  349. {
  350. float target = (float)pixels / (float)pixelsPossible;
  351. target = (target >= 0.95f) ? 1.0f : (target < 0.0f) ? 0.0f : target;
  352. float rate = gpGlobals->frametime * fadeTimeInv;
  353. m_brightnessTarget = Approach( target, m_brightnessTarget, rate ); // fade in / out
  354. }
  355. else
  356. {
  357. m_brightnessTarget = 0.0f;
  358. }
  359. }
  360. else
  361. {
  362. if ( m_frameIssued != -1 )
  363. {
  364. pixels = pRenderContext->OcclusionQuery_GetNumPixelsRendered( m_queryHandle );
  365. }
  366. if ( r_pixelvisibility_spew.GetBool() && CurrentViewID() == 0 )
  367. {
  368. DevMsg( 1, "Pixels visible: %d (qh:%p) (frame:%d)\n", pixels, m_queryHandle, gpGlobals->framecount );
  369. }
  370. if ( pixels < 0 )
  371. {
  372. m_failed = ( m_frameIssued >= 0 ) ? true : false;
  373. return m_brightnessTarget * m_clipFraction;
  374. }
  375. m_hasValidQueryResults = true;
  376. if ( m_frameIssued == gpGlobals->framecount-1 )
  377. {
  378. float rate = gpGlobals->frametime * fadeTimeInv;
  379. float target = 0.0f;
  380. if ( pixels > 0 )
  381. {
  382. // fade in slower than you fade out
  383. rate *= 0.5f;
  384. target = 1.0f;
  385. }
  386. m_brightnessTarget = Approach( target, m_brightnessTarget, rate ); // fade in / out
  387. }
  388. else
  389. {
  390. m_brightnessTarget = 0.0f;
  391. }
  392. }
  393. }
  394. return m_brightnessTarget * m_clipFraction;
  395. }
  396. void CPixelVisibilityQuery::IssueQuery( IMatRenderContext *pRenderContext, float proxySize, float proxyAspect, IMaterial *pMaterial, bool sizeIsScreenSpace )
  397. {
  398. if ( !m_failed )
  399. {
  400. Assert( IsValid() );
  401. if ( r_pixelvisibility_spew.GetBool() && CurrentViewID() == 0 )
  402. {
  403. DevMsg( 1, "Draw Proxy: qh:%p org:<%d,%d,%d> (frame:%d)\n", m_queryHandle, (int)m_origin[0], (int)m_origin[1], (int)m_origin[2], gpGlobals->framecount );
  404. }
  405. m_clipFraction = PixelVisibility_DrawProxy( pRenderContext, m_queryHandle, m_origin, proxySize, proxyAspect, pMaterial, sizeIsScreenSpace );
  406. if ( m_clipFraction < 0 )
  407. {
  408. // NOTE: In this case, the proxy wasn't issued cause it was offscreen
  409. // can't set the m_frameissued[ slot ] field since that would cause it to get marked as failed
  410. m_clipFraction = 0;
  411. m_wasQueriedThisFrame = false;
  412. m_failed = false;
  413. return;
  414. }
  415. }
  416. #ifndef PORTAL // FIXME: In portal we query visibility multiple times per frame because of portal renders!
  417. // In split screen we can issue these multiple times
  418. Assert( m_frameIssued != gpGlobals->framecount);
  419. #endif
  420. m_frameIssued = gpGlobals->framecount;
  421. m_wasQueriedThisFrame = false;
  422. m_failed = false;
  423. }
  424. void CPixelVisibilityQuery::IssueCountingQuery( IMatRenderContext *pRenderContext, float proxySize, float proxyAspect, IMaterial *pMaterial, bool sizeIsScreenSpace )
  425. {
  426. if ( !m_failed )
  427. {
  428. Assert( IsValid() );
  429. #if 0
  430. // this centers it on the screen.
  431. // This is nice because it makes the glows fade as they get partially clipped by the view frustum
  432. // But it introduces sub-pixel errors (off by one row/column of pixels) so the glows shimmer
  433. // UNDONE: Compute an offset center coord that matches sub-pixel coords with the real glow position
  434. // UNDONE: Or frustum clip the sphere/geometry and fade based on proxy size
  435. Vector origin = m_origin - CurrentViewOrigin();
  436. float dot = DotProduct(CurrentViewForward(), origin);
  437. origin = CurrentViewOrigin() + dot * CurrentViewForward();
  438. #endif
  439. PixelVisibility_DrawProxy( pRenderContext, m_queryHandleCount, m_origin, proxySize, proxyAspect, pMaterial, sizeIsScreenSpace );
  440. }
  441. }
  442. //Precache the effects
  443. PRECACHE_REGISTER_BEGIN( GLOBAL, PrecacheOcclusionProxy )
  444. PRECACHE( MATERIAL, "engine/occlusionproxy" )
  445. PRECACHE( MATERIAL, "engine/occlusionproxy_countdraw" )
  446. PRECACHE_REGISTER_END()
  447. class CPixelVisibilitySystem : public CAutoGameSystem
  448. {
  449. public:
  450. // GameSystem: Level init, shutdown
  451. virtual void LevelInitPreEntity();
  452. virtual void LevelShutdownPostEntity();
  453. // locals
  454. CPixelVisibilitySystem();
  455. float GetFractionVisible( const pixelvis_queryparams_t &params, pixelvis_handle_t *queryHandle );
  456. void EndView();
  457. void EndScene();
  458. unsigned short FindQueryForView( CPixelVisSet *pSet, int nPlayerSlot, int viewID );
  459. unsigned short FindOrCreateQueryForView( CPixelVisSet *pSet, int nPlayerSlot, int viewID );
  460. void DeleteUnusedQueries( CPixelVisSet *pSet, bool bDeleteAll );
  461. void DeleteUnusedSets( bool bDeleteAll );
  462. void ShowQueries( bool show );
  463. unsigned short AllocQuery();
  464. unsigned short AllocSet();
  465. void FreeSet( unsigned short node );
  466. CPixelVisSet *FindOrCreatePixelVisSet( const pixelvis_queryparams_t &params, pixelvis_handle_t *queryHandle );
  467. bool SupportsOcclusion() { return m_hwCanTestGlows; }
  468. void DebugInfo()
  469. {
  470. Msg("Pixel vis system using %d sets total (%d in free list), %d queries total (%d in free list)\n",
  471. m_setList.TotalCount(), m_setList.Count(m_freeSetsList), m_queryList.TotalCount(), m_queryList.Count( m_freeQueriesList ) );
  472. }
  473. private:
  474. CUtlMultiList< CPixelVisSet, unsigned short > m_setList;
  475. CUtlMultiList<CPixelVisibilityQuery, unsigned short> m_queryList;
  476. unsigned short m_freeQueriesList;
  477. unsigned short m_activeSetsList;
  478. unsigned short m_freeSetsList;
  479. unsigned short m_pad0;
  480. IMaterial *m_pProxyMaterial;
  481. IMaterial *m_pDrawMaterial;
  482. bool m_hwCanTestGlows;
  483. bool m_drawQueries;
  484. friend void PixelVisibility_ShiftVisibilityViews( int nPlayerSlot, int iSourceViewID, int iDestViewID ); //need direct access to private data to make shifting smooth
  485. };
  486. static CPixelVisibilitySystem g_PixelVisibilitySystem;
  487. CPixelVisibilitySystem::CPixelVisibilitySystem() : CAutoGameSystem( "CPixelVisibilitySystem" )
  488. {
  489. m_hwCanTestGlows = true;
  490. m_drawQueries = false;
  491. }
  492. // Level init, shutdown
  493. void CPixelVisibilitySystem::LevelInitPreEntity()
  494. {
  495. m_hwCanTestGlows = r_dopixelvisibility.GetBool();
  496. if ( m_hwCanTestGlows )
  497. {
  498. CMatRenderContextPtr pRenderContext( materials );
  499. OcclusionQueryObjectHandle_t query = pRenderContext->CreateOcclusionQueryObject();
  500. if ( query != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE )
  501. {
  502. pRenderContext->DestroyOcclusionQueryObject( query );
  503. }
  504. else
  505. {
  506. m_hwCanTestGlows = false;
  507. }
  508. }
  509. m_pProxyMaterial = materials->FindMaterial("engine/occlusionproxy", TEXTURE_GROUP_CLIENT_EFFECTS);
  510. m_pProxyMaterial->IncrementReferenceCount();
  511. m_pDrawMaterial = materials->FindMaterial("engine/occlusionproxy_countdraw", TEXTURE_GROUP_CLIENT_EFFECTS);
  512. m_pDrawMaterial->IncrementReferenceCount();
  513. m_freeQueriesList = m_queryList.CreateList();
  514. m_activeSetsList = m_setList.CreateList();
  515. m_freeSetsList = m_setList.CreateList();
  516. }
  517. void CPixelVisibilitySystem::LevelShutdownPostEntity()
  518. {
  519. m_pProxyMaterial->DecrementReferenceCount();
  520. m_pProxyMaterial = NULL;
  521. m_pDrawMaterial->DecrementReferenceCount();
  522. m_pDrawMaterial = NULL;
  523. DeleteUnusedSets(true);
  524. m_setList.Purge();
  525. m_queryList.Purge();
  526. m_freeQueriesList = m_queryList.InvalidIndex();
  527. m_activeSetsList = m_setList.InvalidIndex();
  528. m_freeSetsList = m_setList.InvalidIndex();
  529. }
  530. float CPixelVisibilitySystem::GetFractionVisible( const pixelvis_queryparams_t &params, pixelvis_handle_t *queryHandle )
  531. {
  532. if ( !m_hwCanTestGlows || building_cubemaps.GetBool() )
  533. {
  534. return GlowSightDistance( params.position, true ) > 0 ? 1.0f : 0.0f;
  535. }
  536. if ( CurrentViewID() < 0 )
  537. return 0.0f;
  538. ASSERT_LOCAL_PLAYER_RESOLVABLE();
  539. int nPlayerSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
  540. CPixelVisSet *pSet = FindOrCreatePixelVisSet( params, queryHandle );
  541. Assert( pSet );
  542. unsigned short node = FindOrCreateQueryForView( pSet, nPlayerSlot, CurrentViewID() );
  543. m_queryList[node].m_origin = params.position;
  544. float fraction = m_queryList[node].GetFractionVisible( pSet->fadeTimeInv );
  545. pSet->MarkActive();
  546. return fraction;
  547. }
  548. void CPixelVisibilitySystem::EndView()
  549. {
  550. if ( !PixelVisibility_IsAvailable() && CurrentViewID() >= 0 )
  551. return;
  552. if ( m_setList.Head( m_activeSetsList ) == m_setList.InvalidIndex() )
  553. return;
  554. ASSERT_LOCAL_PLAYER_RESOLVABLE();
  555. int nPlayerSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
  556. CMatRenderContextPtr pRenderContext( materials );
  557. IMaterial *pProxy = m_drawQueries ? m_pDrawMaterial : m_pProxyMaterial;
  558. pRenderContext->Bind( pProxy );
  559. // BUGBUG: If you draw both queries, the measure query fails for some reason.
  560. if ( r_pixelvisibility_partial.GetBool() && !m_drawQueries )
  561. {
  562. pRenderContext->DepthRange( 0.0f, 0.01f );
  563. unsigned short node = m_setList.Head( m_activeSetsList );
  564. while( node != m_setList.InvalidIndex() )
  565. {
  566. CPixelVisSet *pSet = &m_setList[node];
  567. unsigned short queryNode = FindQueryForView( pSet, nPlayerSlot, CurrentViewID() );
  568. if ( queryNode != m_queryList.InvalidIndex() )
  569. {
  570. m_queryList[queryNode].IssueCountingQuery( pRenderContext, pSet->proxySize, pSet->proxyAspect, pProxy, pSet->sizeIsScreenSpace );
  571. }
  572. node = m_setList.Next( node );
  573. }
  574. pRenderContext->DepthRange( 0.0f, 1.0f );
  575. }
  576. {
  577. unsigned short node = m_setList.Head( m_activeSetsList );
  578. while( node != m_setList.InvalidIndex() )
  579. {
  580. CPixelVisSet *pSet = &m_setList[node];
  581. unsigned short queryNode = FindQueryForView( pSet, nPlayerSlot, CurrentViewID() );
  582. if ( queryNode != m_queryList.InvalidIndex() )
  583. {
  584. m_queryList[queryNode].IssueQuery( pRenderContext, pSet->proxySize, pSet->proxyAspect, pProxy, pSet->sizeIsScreenSpace );
  585. }
  586. node = m_setList.Next( node );
  587. }
  588. }
  589. }
  590. void CPixelVisibilitySystem::EndScene()
  591. {
  592. DeleteUnusedSets(false);
  593. }
  594. unsigned short CPixelVisibilitySystem::FindQueryForView( CPixelVisSet *pSet, int nPlayerSlot, int viewID )
  595. {
  596. unsigned short node = m_queryList.Head( pSet->queryList );
  597. while ( node != m_queryList.InvalidIndex() )
  598. {
  599. if ( m_queryList[node].IsForView( nPlayerSlot, viewID ) )
  600. return node;
  601. node = m_queryList.Next( node );
  602. }
  603. return m_queryList.InvalidIndex();
  604. }
  605. unsigned short CPixelVisibilitySystem::FindOrCreateQueryForView( CPixelVisSet *pSet, int nPlayerSlot, int viewID )
  606. {
  607. unsigned short node = FindQueryForView( pSet, nPlayerSlot, viewID );
  608. if ( node != m_queryList.InvalidIndex() )
  609. return node;
  610. node = AllocQuery();
  611. m_queryList.LinkToHead( pSet->queryList, node );
  612. m_queryList[node].SetView( nPlayerSlot, viewID );
  613. return node;
  614. }
  615. void CPixelVisibilitySystem::DeleteUnusedQueries( CPixelVisSet *pSet, bool bDeleteAll )
  616. {
  617. unsigned short node = m_queryList.Head( pSet->queryList );
  618. while ( node != m_queryList.InvalidIndex() )
  619. {
  620. unsigned short next = m_queryList.Next( node );
  621. if ( bDeleteAll || !m_queryList[node].IsActive() )
  622. {
  623. m_queryList.Unlink( pSet->queryList, node);
  624. m_queryList.LinkToHead( m_freeQueriesList, node );
  625. }
  626. node = next;
  627. }
  628. }
  629. void CPixelVisibilitySystem::DeleteUnusedSets( bool bDeleteAll )
  630. {
  631. unsigned short node = m_setList.Head( m_activeSetsList );
  632. while ( node != m_setList.InvalidIndex() )
  633. {
  634. unsigned short next = m_setList.Next( node );
  635. CPixelVisSet *pSet = &m_setList[node];
  636. if ( bDeleteAll || !m_setList[node].IsActive() )
  637. {
  638. DeleteUnusedQueries( pSet, true );
  639. }
  640. else
  641. {
  642. DeleteUnusedQueries( pSet, false );
  643. }
  644. if ( m_queryList.Head(pSet->queryList) == m_queryList.InvalidIndex() )
  645. {
  646. FreeSet( node );
  647. }
  648. node = next;
  649. }
  650. }
  651. void CPixelVisibilitySystem::ShowQueries( bool show )
  652. {
  653. m_drawQueries = show;
  654. }
  655. unsigned short CPixelVisibilitySystem::AllocQuery()
  656. {
  657. unsigned short node = m_queryList.Head(m_freeQueriesList);
  658. if ( node != m_queryList.InvalidIndex() )
  659. {
  660. m_queryList.Unlink( m_freeQueriesList, node );
  661. m_queryList[node].ResetOcclusionQueries();
  662. }
  663. else
  664. {
  665. node = m_queryList.Alloc();
  666. }
  667. return node;
  668. }
  669. unsigned short CPixelVisibilitySystem::AllocSet()
  670. {
  671. unsigned short node = m_setList.Head(m_freeSetsList);
  672. if ( node != m_setList.InvalidIndex() )
  673. {
  674. m_setList.Unlink( m_freeSetsList, node );
  675. }
  676. else
  677. {
  678. node = m_setList.Alloc();
  679. m_setList[node].queryList = m_queryList.CreateList();
  680. }
  681. m_setList.LinkToHead( m_activeSetsList, node );
  682. return node;
  683. }
  684. void CPixelVisibilitySystem::FreeSet( unsigned short node )
  685. {
  686. m_setList.Unlink( m_activeSetsList, node );
  687. m_setList.LinkToHead( m_freeSetsList, node );
  688. m_setList[node].serial++;
  689. }
  690. CPixelVisSet *CPixelVisibilitySystem::FindOrCreatePixelVisSet( const pixelvis_queryparams_t &params, pixelvis_handle_t *queryHandle )
  691. {
  692. if ( queryHandle[0] )
  693. {
  694. unsigned short handle = queryHandle[0] & 0xFFFF;
  695. handle--;
  696. unsigned short serial = queryHandle[0] >> 16;
  697. if ( m_setList.IsValidIndex(handle) && m_setList[handle].serial == serial )
  698. {
  699. return &m_setList[handle];
  700. }
  701. }
  702. unsigned short node = AllocSet();
  703. m_setList[node].Init( params );
  704. unsigned int out = m_setList[node].serial;
  705. unsigned short nodeHandle = node + 1;
  706. out <<= 16;
  707. out |= nodeHandle;
  708. queryHandle[0] = out;
  709. return &m_setList[node];
  710. }
  711. void PixelvisDrawChanged( IConVar *pPixelvisVar, const char *pOld, float flOldValue )
  712. {
  713. ConVarRef var( pPixelvisVar );
  714. g_PixelVisibilitySystem.ShowQueries( var.GetBool() );
  715. }
  716. class CTraceFilterGlow : public CTraceFilterSimple
  717. {
  718. public:
  719. DECLARE_CLASS( CTraceFilterGlow, CTraceFilterSimple );
  720. CTraceFilterGlow( const IHandleEntity *passentity, int collisionGroup ) : CTraceFilterSimple(passentity, collisionGroup) {}
  721. virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  722. {
  723. IClientUnknown *pUnk = (IClientUnknown*)pHandleEntity;
  724. ICollideable *pCollide = pUnk->GetCollideable();
  725. if ( pCollide->GetSolid() != SOLID_VPHYSICS && pCollide->GetSolid() != SOLID_BSP )
  726. return false;
  727. return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask );
  728. }
  729. };
  730. float GlowSightDistance( const Vector &glowOrigin, bool bShouldTrace )
  731. {
  732. float dist = (glowOrigin - CurrentViewOrigin()).Length();
  733. C_BasePlayer *local = C_BasePlayer::GetLocalPlayer();
  734. if ( local )
  735. {
  736. dist *= local->GetFOVDistanceAdjustFactor();
  737. }
  738. if ( bShouldTrace )
  739. {
  740. Vector end = glowOrigin;
  741. // HACKHACK: trace 4" from destination in case the glow is inside some parent object
  742. // allow a little error...
  743. if ( dist > 4 )
  744. {
  745. end -= CurrentViewForward()*4;
  746. }
  747. int traceFlags = MASK_OPAQUE|CONTENTS_MONSTER|CONTENTS_DEBRIS;
  748. CTraceFilterGlow filter(NULL, COLLISION_GROUP_NONE);
  749. trace_t tr;
  750. UTIL_TraceLine( CurrentViewOrigin(), end, traceFlags, &filter, &tr );
  751. if ( tr.fraction != 1.0f )
  752. return -1;
  753. }
  754. return dist;
  755. }
  756. void PixelVisibility_EndCurrentView()
  757. {
  758. g_PixelVisibilitySystem.EndView();
  759. }
  760. void PixelVisibility_EndScene()
  761. {
  762. g_PixelVisibilitySystem.EndScene();
  763. }
  764. float PixelVisibility_FractionVisible( const pixelvis_queryparams_t &params, pixelvis_handle_t *queryHandle )
  765. {
  766. if ( !queryHandle )
  767. {
  768. return GlowSightDistance( params.position, true ) > 0.0f ? 1.0f : 0.0f;
  769. }
  770. else
  771. {
  772. return g_PixelVisibilitySystem.GetFractionVisible( params, queryHandle );
  773. }
  774. }
  775. bool PixelVisibility_IsAvailable()
  776. {
  777. return r_dopixelvisibility.GetBool() && g_PixelVisibilitySystem.SupportsOcclusion();
  778. }
  779. //this originally called a class function of CPixelVisibiltySystem to keep the work clean, but that function needed friend access to CPixelVisibilityQuery
  780. //and I didn't want to make the whole class a friend or shift all the functions and class declarations around in this file
  781. void PixelVisibility_ShiftVisibilityViews( int nPlayerSlot, int iSourceViewID, int iDestViewID )
  782. {
  783. unsigned short node = g_PixelVisibilitySystem.m_setList.Head( g_PixelVisibilitySystem.m_activeSetsList );
  784. while ( node != g_PixelVisibilitySystem.m_setList.InvalidIndex() )
  785. {
  786. unsigned short next = g_PixelVisibilitySystem.m_setList.Next( node );
  787. CPixelVisSet *pSet = &g_PixelVisibilitySystem.m_setList[node];
  788. unsigned short iSourceQueryNode = g_PixelVisibilitySystem.FindQueryForView( pSet, nPlayerSlot, iSourceViewID );
  789. unsigned short iDestQueryNode = g_PixelVisibilitySystem.FindQueryForView( pSet, nPlayerSlot, iDestViewID );
  790. if( iDestQueryNode != g_PixelVisibilitySystem.m_queryList.InvalidIndex() )
  791. {
  792. //delete the destination if found
  793. g_PixelVisibilitySystem.m_queryList.Unlink( pSet->queryList, iDestQueryNode );
  794. g_PixelVisibilitySystem.m_queryList.LinkToHead( g_PixelVisibilitySystem.m_freeQueriesList, iDestQueryNode );
  795. if ( g_PixelVisibilitySystem.m_queryList.Head(pSet->queryList) == g_PixelVisibilitySystem.m_queryList.InvalidIndex() )
  796. {
  797. g_PixelVisibilitySystem.FreeSet( node );
  798. }
  799. }
  800. if( iSourceQueryNode != g_PixelVisibilitySystem.m_queryList.InvalidIndex() )
  801. {
  802. //make the source believe it's the destination
  803. g_PixelVisibilitySystem.m_queryList[iSourceQueryNode].m_viewID = iDestViewID;
  804. }
  805. node = next;
  806. }
  807. for( int i = 0; i != s_OcclusionQueries.Count(); ++i )
  808. {
  809. int iPairCount = s_OcclusionQueries[i].occlusionHandles[nPlayerSlot].Count();
  810. OcclusionHandleViewIDPair_t *pPairs = s_OcclusionQueries[i].occlusionHandles[nPlayerSlot].Base();
  811. int iSourceIndex, iDestIndex, iInvalidIndex;
  812. iDestIndex = iSourceIndex = iInvalidIndex = s_OcclusionQueries[i].occlusionHandles[nPlayerSlot].InvalidIndex();
  813. for( int j = 0; j != iPairCount; ++j )
  814. {
  815. if( pPairs[j].iViewID == iSourceViewID )
  816. {
  817. iSourceIndex = j;
  818. if( iDestIndex != iInvalidIndex )
  819. break;
  820. }
  821. if( pPairs[j].iViewID == iDestViewID )
  822. {
  823. iDestIndex = j;
  824. if( iSourceIndex != iInvalidIndex )
  825. break;
  826. }
  827. }
  828. if( iSourceIndex != iInvalidIndex )
  829. {
  830. //change view id on source
  831. pPairs[iSourceIndex].iViewID = iDestViewID;
  832. }
  833. if( iDestIndex != iInvalidIndex )
  834. {
  835. //destroy dest
  836. materials->GetRenderContext()->DestroyOcclusionQueryObject( pPairs[iDestIndex].hOcclusionHandle );
  837. s_OcclusionQueries[i].occlusionHandles[nPlayerSlot].FastRemove( iDestIndex );
  838. }
  839. }
  840. }
  841. COcclusionQuerySet::COcclusionQuerySet( void )
  842. {
  843. OcclusionQueryHiddenData_t &data = s_OcclusionQueries[s_OcclusionQueries.AddToTail()];
  844. data.pOwner = this;
  845. m_pManagedData = &data;
  846. //handle base address shifting
  847. if( s_OcclusionQueries.Count() > 1 )
  848. {
  849. OcclusionQueryHiddenData_t &baseData = s_OcclusionQueries[0];
  850. if( baseData.pOwner->m_pManagedData != &baseData )
  851. {
  852. for( int i = 0; i != s_OcclusionQueries.Count(); ++i )
  853. {
  854. s_OcclusionQueries[i].pOwner->m_pManagedData = &s_OcclusionQueries[i];
  855. }
  856. }
  857. }
  858. }
  859. COcclusionQuerySet::~COcclusionQuerySet( void )
  860. {
  861. int iIndex;
  862. for( iIndex = 0; iIndex != s_OcclusionQueries.Count(); ++iIndex )
  863. {
  864. if( &s_OcclusionQueries[iIndex] == m_pManagedData )
  865. break;
  866. }
  867. if( iIndex != s_OcclusionQueries.Count() )
  868. {
  869. //destroy query handles
  870. {
  871. CMatRenderContextPtr pRenderContext( materials );
  872. OcclusionQueryHiddenData_t &data = s_OcclusionQueries[iIndex];
  873. for( int i = 0; i != MAX_SPLITSCREEN_PLAYERS; ++i )
  874. {
  875. for( int j = 0; j != data.occlusionHandles[i].Count(); ++j )
  876. {
  877. pRenderContext->DestroyOcclusionQueryObject( data.occlusionHandles[i].Element(j).hOcclusionHandle );
  878. }
  879. }
  880. }
  881. s_OcclusionQueries.FastRemove( iIndex );
  882. if( s_OcclusionQueries.Count() != 0 )
  883. {
  884. s_OcclusionQueries[iIndex].pOwner->m_pManagedData = &s_OcclusionQueries[iIndex];
  885. //handle base address shifting
  886. if( s_OcclusionQueries.Count() > 1 )
  887. {
  888. OcclusionQueryHiddenData_t &baseData = s_OcclusionQueries[iIndex == 0 ? 1 : 0];
  889. if( baseData.pOwner->m_pManagedData != &baseData )
  890. {
  891. for( int i = 0; i != s_OcclusionQueries.Count(); ++i )
  892. {
  893. s_OcclusionQueries[i].pOwner->m_pManagedData = &s_OcclusionQueries[i];
  894. }
  895. }
  896. }
  897. }
  898. }
  899. }
  900. void COcclusionQuerySet::BeginQueryDrawing( int iViewID, int iSplitScreenSlot )
  901. {
  902. OcclusionQueryHiddenData_t *pData = (OcclusionQueryHiddenData_t *)m_pManagedData;
  903. int iIndex = FindQueryHandlePairIndex( pData, iViewID, iSplitScreenSlot );
  904. if( iIndex == pData->occlusionHandles[iSplitScreenSlot].InvalidIndex() )
  905. {
  906. //create a new one
  907. iIndex = pData->occlusionHandles[iSplitScreenSlot].AddToTail();
  908. OcclusionHandleViewIDPair_t &Entry = pData->occlusionHandles[iSplitScreenSlot].Element(iIndex);
  909. Entry.iViewID = iViewID;
  910. Entry.hOcclusionHandle = materials->GetRenderContext()->CreateOcclusionQueryObject();
  911. materials->GetRenderContext()->ResetOcclusionQueryObject( Entry.hOcclusionHandle );
  912. }
  913. materials->GetRenderContext()->BeginOcclusionQueryDrawing( pData->occlusionHandles[iSplitScreenSlot].Element(iIndex).hOcclusionHandle );
  914. pData->occlusionHandles[iSplitScreenSlot].Element(iIndex).iLastFrameRendered = gpGlobals->framecount;
  915. }
  916. void COcclusionQuerySet::BeginQueryDrawing( void )
  917. {
  918. return BeginQueryDrawing( CurrentViewID(), GET_ACTIVE_SPLITSCREEN_SLOT() );
  919. }
  920. void COcclusionQuerySet::EndQueryDrawing( int iViewID, int iSplitScreenSlot )
  921. {
  922. OcclusionQueryHiddenData_t *pData = (OcclusionQueryHiddenData_t *)m_pManagedData;
  923. int iIndex = FindQueryHandlePairIndex( pData, iViewID, iSplitScreenSlot );
  924. if( iIndex != pData->occlusionHandles[iSplitScreenSlot].InvalidIndex() )
  925. {
  926. materials->GetRenderContext()->EndOcclusionQueryDrawing( pData->occlusionHandles[iSplitScreenSlot].Element(iIndex).hOcclusionHandle );
  927. }
  928. }
  929. void COcclusionQuerySet::EndQueryDrawing( void )
  930. {
  931. return EndQueryDrawing( CurrentViewID(), GET_ACTIVE_SPLITSCREEN_SLOT() );
  932. }
  933. int COcclusionQuerySet::QueryNumPixelsRendered( int iViewID, int iSplitScreenSlot )
  934. {
  935. OcclusionQueryHiddenData_t *pData = (OcclusionQueryHiddenData_t *)m_pManagedData;
  936. int iIndex = FindQueryHandlePairIndex( pData, iViewID, iSplitScreenSlot );
  937. if( iIndex != pData->occlusionHandles[iSplitScreenSlot].InvalidIndex() )
  938. {
  939. return materials->GetRenderContext()->OcclusionQuery_GetNumPixelsRendered( pData->occlusionHandles[iSplitScreenSlot].Element(iIndex).hOcclusionHandle );
  940. }
  941. return 0;
  942. }
  943. int COcclusionQuerySet::QueryNumPixelsRendered( void )
  944. {
  945. return QueryNumPixelsRendered( CurrentViewID(), GET_ACTIVE_SPLITSCREEN_SLOT() );
  946. }
  947. float COcclusionQuerySet::QueryPercentageOfScreenRendered( int iViewID, int iSplitScreenSlot )
  948. {
  949. OcclusionQueryHiddenData_t *pData = (OcclusionQueryHiddenData_t *)m_pManagedData;
  950. int iIndex = FindQueryHandlePairIndex( pData, iViewID, iSplitScreenSlot );
  951. if( iIndex != pData->occlusionHandles[iSplitScreenSlot].InvalidIndex() )
  952. {
  953. int iX, iY, iWidth, iHeight;
  954. materials->GetRenderContext()->GetViewport( iX, iY, iWidth, iHeight );
  955. return ((float)materials->GetRenderContext()->OcclusionQuery_GetNumPixelsRendered( pData->occlusionHandles[iSplitScreenSlot].Element(iIndex).hOcclusionHandle )) / ((float)(iWidth * iHeight));
  956. }
  957. return 0.0f;
  958. }
  959. float COcclusionQuerySet::QueryPercentageOfScreenRendered( void )
  960. {
  961. return QueryPercentageOfScreenRendered( CurrentViewID(), GET_ACTIVE_SPLITSCREEN_SLOT() );
  962. }
  963. int COcclusionQuerySet::QueryNumPixelsRenderedForAllViewsLastFrame( int iSplitScreenSlot )
  964. {
  965. OcclusionQueryHiddenData_t *pData = (OcclusionQueryHiddenData_t *)m_pManagedData;
  966. int iMatchFrame = gpGlobals->framecount - 1;
  967. int iResult = 0;
  968. CMatRenderContextPtr pRenderContext( materials );
  969. for( int i = 0; i != pData->occlusionHandles[iSplitScreenSlot].Count(); ++i )
  970. {
  971. if( pData->occlusionHandles[iSplitScreenSlot].Element(i).iLastFrameRendered == iMatchFrame )
  972. {
  973. iResult += pRenderContext->OcclusionQuery_GetNumPixelsRendered( pData->occlusionHandles[iSplitScreenSlot].Element(i).hOcclusionHandle );
  974. }
  975. }
  976. return iResult;
  977. }
  978. int COcclusionQuerySet::QueryNumPixelsRenderedForAllViewsLastFrame( void )
  979. {
  980. return QueryNumPixelsRenderedForAllViewsLastFrame( GET_ACTIVE_SPLITSCREEN_SLOT() );
  981. }
  982. int COcclusionQuerySet::GetLastFrameDrawn( int iViewID, int iSplitScreenSlot )
  983. {
  984. OcclusionQueryHiddenData_t *pData = (OcclusionQueryHiddenData_t *)m_pManagedData;
  985. int iIndex = FindQueryHandlePairIndex( pData, iViewID, iSplitScreenSlot );
  986. if( iIndex != pData->occlusionHandles[iSplitScreenSlot].InvalidIndex() )
  987. {
  988. return pData->occlusionHandles[iSplitScreenSlot].Element(iIndex).iLastFrameRendered;
  989. }
  990. return -1;
  991. }
  992. int COcclusionQuerySet::GetLastFrameDrawn( void )
  993. {
  994. return GetLastFrameDrawn( CurrentViewID(), GET_ACTIVE_SPLITSCREEN_SLOT() );
  995. }
  996. CON_COMMAND( pixelvis_debug, "Dump debug info" )
  997. {
  998. g_PixelVisibilitySystem.DebugInfo();
  999. }