source-engine/materialsystem/occlusionquerymgr.cpp
2022-06-05 01:12:32 +03:00

259 lines
9.1 KiB
C++

//========= 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;
}