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.1 KiB
258 lines
9.1 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//============================================================================= |
|
|
|
#include "pch_materialsystem.h" |
|
|
|
#define MATSYS_INTERNAL |
|
|
|
#include "occlusionquerymgr.h" |
|
#include "imaterialsysteminternal.h" |
|
#include "imatrendercontextinternal.h" |
|
|
|
// NOTE: This must be the last file included!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Singleton |
|
//----------------------------------------------------------------------------- |
|
static COcclusionQueryMgr s_OcclusionQueryMgr; |
|
COcclusionQueryMgr *g_pOcclusionQueryMgr = &s_OcclusionQueryMgr; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Constructor |
|
//----------------------------------------------------------------------------- |
|
COcclusionQueryMgr::COcclusionQueryMgr() |
|
{ |
|
m_nFrameCount = 0; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Allocate and delete query objects. |
|
//----------------------------------------------------------------------------- |
|
OcclusionQueryObjectHandle_t COcclusionQueryMgr::CreateOcclusionQueryObject( ) |
|
{ |
|
m_Mutex.Lock(); |
|
intp h = m_OcclusionQueryObjects.AddToTail(); |
|
m_Mutex.Unlock(); |
|
return (OcclusionQueryObjectHandle_t)h; |
|
} |
|
|
|
void COcclusionQueryMgr::OnCreateOcclusionQueryObject( OcclusionQueryObjectHandle_t h ) |
|
{ |
|
for ( int i = 0; i < COUNT_OCCLUSION_QUERY_STACK; i++) |
|
{ |
|
m_OcclusionQueryObjects[(intp)h].m_QueryHandle[i] = g_pShaderAPI->CreateOcclusionQueryObject( ); |
|
} |
|
} |
|
|
|
// Flushes an outstanding query |
|
// HEY - Be very careful using this method - it causes a full pipeline flush/stall! |
|
void COcclusionQueryMgr::FlushQuery( OcclusionQueryObjectHandle_t hOcclusionQuery, int nIndex ) |
|
{ |
|
// Flush out any previous queries |
|
intp h = (intp)hOcclusionQuery; |
|
if ( m_OcclusionQueryObjects[h].m_bHasBeenIssued[nIndex] ) |
|
{ |
|
ShaderAPIOcclusionQuery_t hQuery = m_OcclusionQueryObjects[h].m_QueryHandle[nIndex]; |
|
|
|
while ( OCCLUSION_QUERY_RESULT_PENDING == g_pShaderAPI->OcclusionQuery_GetNumPixelsRendered( hQuery, true ) ) |
|
continue; |
|
} |
|
} |
|
|
|
void COcclusionQueryMgr::DestroyOcclusionQueryObject( OcclusionQueryObjectHandle_t hOcclusionQuery ) |
|
{ |
|
intp h = (intp)hOcclusionQuery; |
|
Assert( m_OcclusionQueryObjects.IsValidIndex( h ) ); |
|
if ( m_OcclusionQueryObjects.IsValidIndex( h ) ) |
|
{ |
|
for ( int i = 0; i < COUNT_OCCLUSION_QUERY_STACK; i++) |
|
{ |
|
if ( m_OcclusionQueryObjects[h].m_QueryHandle[i] != INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE ) |
|
{ |
|
g_pShaderAPI->DestroyOcclusionQueryObject( m_OcclusionQueryObjects[h].m_QueryHandle[i] ); |
|
} |
|
} |
|
m_Mutex.Lock(); |
|
m_OcclusionQueryObjects.Remove( h ); |
|
m_Mutex.Unlock(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Advance frame |
|
//----------------------------------------------------------------------------- |
|
void COcclusionQueryMgr::AdvanceFrame() |
|
{ |
|
++m_nFrameCount; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Alt-tab support |
|
// NOTE: This doesn't queue anything up |
|
//----------------------------------------------------------------------------- |
|
void COcclusionQueryMgr::AllocOcclusionQueryObjects( void ) |
|
{ |
|
FOR_EACH_LL( m_OcclusionQueryObjects, iterator ) |
|
{ |
|
for ( int i = 0; i < COUNT_OCCLUSION_QUERY_STACK; i++) |
|
{ |
|
m_OcclusionQueryObjects[iterator].m_QueryHandle[i] = g_pShaderAPI->CreateOcclusionQueryObject(); |
|
m_OcclusionQueryObjects[iterator].m_bHasBeenIssued[i] = false; // any in-flight queries are never returning |
|
} |
|
} |
|
} |
|
|
|
void COcclusionQueryMgr::FreeOcclusionQueryObjects( void ) |
|
{ |
|
FOR_EACH_LL( m_OcclusionQueryObjects, iterator ) |
|
{ |
|
for ( int i = 0; i < COUNT_OCCLUSION_QUERY_STACK; i++) |
|
{ |
|
if ( m_OcclusionQueryObjects[iterator].m_QueryHandle[i] != INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE ) |
|
{ |
|
g_pShaderAPI->DestroyOcclusionQueryObject( m_OcclusionQueryObjects[iterator].m_QueryHandle[i] ); |
|
m_OcclusionQueryObjects[iterator].m_QueryHandle[i] = INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE; |
|
m_OcclusionQueryObjects[iterator].m_bHasBeenIssued[i] = false; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Used to make the handle think it's never had a successful query before |
|
//----------------------------------------------------------------------------- |
|
void COcclusionQueryMgr::ResetOcclusionQueryObject( OcclusionQueryObjectHandle_t hOcclusionQuery ) |
|
{ |
|
intp h = (intp)hOcclusionQuery; |
|
Assert( m_OcclusionQueryObjects.IsValidIndex( h ) ); |
|
if ( m_OcclusionQueryObjects.IsValidIndex( h ) ) |
|
{ |
|
// Forget we've issued any previous queries - there's no need to flush them. |
|
for ( int i = 0; i < COUNT_OCCLUSION_QUERY_STACK; i++) |
|
{ |
|
m_OcclusionQueryObjects[h].m_bHasBeenIssued[i] = false; |
|
} |
|
|
|
m_OcclusionQueryObjects[h].m_LastResult = -1; |
|
m_OcclusionQueryObjects[h].m_nFrameIssued = -1; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Bracket drawing with begin and end so that we can get counts next frame. |
|
//----------------------------------------------------------------------------- |
|
void COcclusionQueryMgr::BeginOcclusionQueryDrawing( OcclusionQueryObjectHandle_t hOcclusionQuery ) |
|
{ |
|
intp h = (intp)hOcclusionQuery; |
|
Assert( m_OcclusionQueryObjects.IsValidIndex( h ) ); |
|
if ( m_OcclusionQueryObjects.IsValidIndex( h ) ) |
|
{ |
|
int nCurrent = m_OcclusionQueryObjects[h].m_nCurrentIssue; |
|
ShaderAPIOcclusionQuery_t hQuery = m_OcclusionQueryObjects[h].m_QueryHandle[nCurrent]; |
|
if ( hQuery != INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE ) |
|
{ |
|
// If it's been issued, but we haven't gotten a result when we polled last time, |
|
// try polling one last time, since we can't poll again after we issue again. |
|
if ( m_OcclusionQueryObjects[h].m_bHasBeenIssued[nCurrent] ) |
|
{ |
|
int nPixels = g_pShaderAPI->OcclusionQuery_GetNumPixelsRendered( hQuery, false ); |
|
if ( ( nPixels == OCCLUSION_QUERY_RESULT_PENDING ) && ( m_OcclusionQueryObjects[h].m_nFrameIssued == m_nFrameCount ) ) |
|
{ |
|
static int s_nWarnCount = 0; |
|
if ( s_nWarnCount++ < 5 ) |
|
{ |
|
DevWarning( "blocking issue in occlusion queries! Grab brian!\n" ); |
|
} |
|
} |
|
while( !OCCLUSION_QUERY_FINISHED( nPixels ) ) |
|
{ |
|
// We're going to reuse this query, so issue a flush to force the query results to come back. |
|
nPixels = g_pShaderAPI->OcclusionQuery_GetNumPixelsRendered( hQuery, true ); |
|
} |
|
if ( nPixels >= 0 ) |
|
{ |
|
m_OcclusionQueryObjects[h].m_LastResult = nPixels; |
|
} |
|
m_OcclusionQueryObjects[h].m_bHasBeenIssued[nCurrent] = false; |
|
} |
|
g_pShaderAPI->BeginOcclusionQueryDrawing( hQuery ); |
|
} |
|
} |
|
} |
|
|
|
|
|
void COcclusionQueryMgr::EndOcclusionQueryDrawing( OcclusionQueryObjectHandle_t hOcclusionQuery ) |
|
{ |
|
intp h = (intp)hOcclusionQuery; |
|
Assert( m_OcclusionQueryObjects.IsValidIndex( h ) ); |
|
if ( m_OcclusionQueryObjects.IsValidIndex( h ) ) |
|
{ |
|
int nCurrent = m_OcclusionQueryObjects[h].m_nCurrentIssue; |
|
ShaderAPIOcclusionQuery_t hQuery = m_OcclusionQueryObjects[h].m_QueryHandle[nCurrent]; |
|
if ( hQuery != INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE ) |
|
{ |
|
g_pShaderAPI->EndOcclusionQueryDrawing( hQuery ); |
|
|
|
m_OcclusionQueryObjects[h].m_bHasBeenIssued[nCurrent] = true; |
|
m_OcclusionQueryObjects[h].m_nFrameIssued = m_nFrameCount; |
|
|
|
nCurrent = ( nCurrent + 1 ) % COUNT_OCCLUSION_QUERY_STACK; |
|
m_OcclusionQueryObjects[h].m_nCurrentIssue = nCurrent; |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Get the number of pixels rendered between begin and end on an earlier frame. |
|
// Calling this in the same frame is a huge perf hit! |
|
//----------------------------------------------------------------------------- |
|
void COcclusionQueryMgr::OcclusionQuery_IssueNumPixelsRenderedQuery( OcclusionQueryObjectHandle_t hOcclusionQuery ) |
|
{ |
|
intp h = (intp)hOcclusionQuery; |
|
Assert( m_OcclusionQueryObjects.IsValidIndex( h ) ); |
|
if ( m_OcclusionQueryObjects.IsValidIndex( h ) ) |
|
{ |
|
for( int i = 0; i < COUNT_OCCLUSION_QUERY_STACK; i++ ) |
|
{ |
|
int nIndex = ( m_OcclusionQueryObjects[h].m_nCurrentIssue + i ) % COUNT_OCCLUSION_QUERY_STACK; |
|
ShaderAPIOcclusionQuery_t hQuery = m_OcclusionQueryObjects[h].m_QueryHandle[nIndex]; |
|
if ( hQuery != INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE && m_OcclusionQueryObjects[h].m_bHasBeenIssued[nIndex] ) |
|
{ |
|
int nPixels = g_pShaderAPI->OcclusionQuery_GetNumPixelsRendered( hQuery ); |
|
if ( nPixels == OCCLUSION_QUERY_RESULT_ERROR ) |
|
{ |
|
// 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. |
|
m_OcclusionQueryObjects[h].m_bHasBeenIssued[nIndex] = false; |
|
} |
|
else if ( nPixels >= 0 ) |
|
{ |
|
m_OcclusionQueryObjects[h].m_LastResult = nPixels; |
|
m_OcclusionQueryObjects[h].m_bHasBeenIssued[nIndex] = false; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
int COcclusionQueryMgr::OcclusionQuery_GetNumPixelsRendered( OcclusionQueryObjectHandle_t h, bool bDoQuery ) |
|
{ |
|
if ( bDoQuery ) |
|
{ |
|
OcclusionQuery_IssueNumPixelsRenderedQuery( h ); |
|
} |
|
|
|
int nPixels = m_OcclusionQueryObjects[(intp)h].m_LastResult; |
|
return nPixels; |
|
}
|
|
|