// Level init, shutdown
void CPixelVisibilitySystem::LevelInitPreEntity()
{
	bool fastqueries = HasFastQueries();
	// printf("\n ** fast queries: %s **", fastqueries?"true":"false" );
	
	m_hwCanTestGlows = r_dopixelvisibility.GetBool() && fastqueries && engine->GetDXSupportLevel() >= 80;
	if ( m_hwCanTestGlows )
	{
		CMatRenderContextPtr pRenderContext( materials );

		OcclusionQueryObjectHandle_t query = pRenderContext->CreateOcclusionQueryObject();
		if ( query != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE )
		{
			pRenderContext->DestroyOcclusionQueryObject( query );
		}
		else
		{
			m_hwCanTestGlows = false;
		}
	}

	m_pProxyMaterial = materials->FindMaterial("engine/occlusionproxy", TEXTURE_GROUP_CLIENT_EFFECTS);
	m_pProxyMaterial->IncrementReferenceCount();
	m_pDrawMaterial = materials->FindMaterial("engine/occlusionproxy_countdraw", TEXTURE_GROUP_CLIENT_EFFECTS);
	m_pDrawMaterial->IncrementReferenceCount();
	m_freeQueriesList = m_queryList.CreateList();
	m_activeSetsList = m_setList.CreateList();
	m_freeSetsList = m_setList.CreateList();
}
unsigned short CPixelVisibilitySystem::FindOrCreateQueryForView( CPixelVisSet *pSet, int viewID )
{
	unsigned short node = FindQueryForView( pSet, viewID );
	if ( node != m_queryList.InvalidIndex() )
		return node;

	node = AllocQuery();
	m_queryList.LinkToHead( pSet->queryList, node );
	m_queryList[node].SetView( viewID );
	return node;
}
unsigned short CPixelVisibilitySystem::FindQueryForView( CPixelVisSet *pSet, int viewID )
{
	unsigned short node = m_queryList.Head( pSet->queryList );
	while ( node != m_queryList.InvalidIndex() )
	{
		if ( m_queryList[node].IsForView( viewID ) )
			return node;
		node = m_queryList.Next( node );
	}
	return m_queryList.InvalidIndex();
}
void CPixelVisibilitySystem::LevelShutdownPostEntity()
{
	m_pProxyMaterial->DecrementReferenceCount();
	m_pProxyMaterial = NULL;
	m_pDrawMaterial->DecrementReferenceCount();
	m_pDrawMaterial = NULL;
	DeleteUnusedSets(true);
	m_setList.Purge();
	m_queryList.Purge();
	m_freeQueriesList = m_queryList.InvalidIndex();
	m_activeSetsList = m_setList.InvalidIndex();
	m_freeSetsList = m_setList.InvalidIndex();
}
unsigned short CPixelVisibilitySystem::AllocQuery()
{
	unsigned short node = m_queryList.Head(m_freeQueriesList);
	if ( node != m_queryList.InvalidIndex() )
	{
		m_queryList.Unlink( m_freeQueriesList, node );
	}
	else
	{
		node = m_queryList.Alloc();
	}
	return node;
}
void CPixelVisibilitySystem::DeleteUnusedQueries( CPixelVisSet *pSet, bool bDeleteAll )
{
	unsigned short node = m_queryList.Head( pSet->queryList );
	while ( node != m_queryList.InvalidIndex() )
	{
		unsigned short next = m_queryList.Next( node );
		if ( bDeleteAll || !m_queryList[node].IsActive() )
		{
			m_queryList.Unlink( pSet->queryList, node);
			m_queryList.LinkToHead( m_freeQueriesList, node );
		}
		node = next;
	}
}
void CPixelVisibilitySystem::EndView()
{
	if ( !PixelVisibility_IsAvailable() && CurrentViewID() >= 0 )
		return;
	
	if ( m_setList.Head( m_activeSetsList ) == m_setList.InvalidIndex() )
		return;

	CMatRenderContextPtr pRenderContext( materials );

	IMaterial *pProxy = m_drawQueries ? m_pDrawMaterial : m_pProxyMaterial;
	pRenderContext->Bind( pProxy );

	// BUGBUG: If you draw both queries, the measure query fails for some reason.
	if ( r_pixelvisibility_partial.GetBool() && !m_drawQueries )
	{
		pRenderContext->DepthRange( 0.0f, 0.01f );
		unsigned short node = m_setList.Head( m_activeSetsList );
		while( node != m_setList.InvalidIndex() )
		{
			CPixelVisSet *pSet = &m_setList[node];
			unsigned short queryNode = FindQueryForView( pSet, CurrentViewID() );
			if ( queryNode != m_queryList.InvalidIndex() )
			{
				m_queryList[queryNode].IssueCountingQuery( pRenderContext, pSet->proxySize, pSet->proxyAspect, pProxy, pSet->sizeIsScreenSpace );
			}
			node = m_setList.Next( node );
		}
		pRenderContext->DepthRange( 0.0f, 1.0f );
	}

	{
		unsigned short node = m_setList.Head( m_activeSetsList );
		while( node != m_setList.InvalidIndex() )
		{
			CPixelVisSet *pSet = &m_setList[node];
			unsigned short queryNode = FindQueryForView( pSet, CurrentViewID() );
			if ( queryNode != m_queryList.InvalidIndex() )
			{
				m_queryList[queryNode].IssueQuery( pRenderContext, pSet->proxySize, pSet->proxyAspect, pProxy, pSet->sizeIsScreenSpace );
			}
			node = m_setList.Next( node );
		}
	}
}
void CPixelVisibilitySystem::DeleteUnusedSets( bool bDeleteAll )
{
	unsigned short node = m_setList.Head( m_activeSetsList );
	while ( node != m_setList.InvalidIndex() )
	{
		unsigned short next = m_setList.Next( node );
		CPixelVisSet *pSet = &m_setList[node];
		if ( bDeleteAll || !m_setList[node].IsActive() )
		{
			DeleteUnusedQueries( pSet, true );
		}
		else
		{
			DeleteUnusedQueries( pSet, false );
		}
		if ( m_queryList.Head(pSet->queryList) == m_queryList.InvalidIndex() )
		{
			FreeSet( node );
		}
		node = next;
	}
}
// Level init, shutdown
void CPixelVisibilitySystem::LevelInitPreEntity()
{
	m_hwCanTestGlows = r_dopixelvisibility.GetBool() && engine->GetDXSupportLevel() >= 80;
	if ( m_hwCanTestGlows )
	{
		unsigned short query = materials->CreateOcclusionQueryObject();
		if ( query != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE )
		{
			materials->DestroyOcclusionQueryObject( query );
		}
		else
		{
			m_hwCanTestGlows = false;
		}
	}

	m_pProxyMaterial = materials->FindMaterial("engine/occlusionproxy", TEXTURE_GROUP_CLIENT_EFFECTS);
	m_pProxyMaterial->IncrementReferenceCount();
	m_pDrawMaterial = materials->FindMaterial("engine/occlusionproxy_countdraw", TEXTURE_GROUP_CLIENT_EFFECTS);
	m_pDrawMaterial->IncrementReferenceCount();
	m_freeQueriesList = m_queryList.CreateList();
	m_activeSetsList = m_setList.CreateList();
	m_freeSetsList = m_setList.CreateList();
}
unsigned short CPixelVisibilitySystem::AllocSet()
{
	unsigned short node = m_setList.Head(m_freeSetsList);
	if ( node != m_setList.InvalidIndex() )
	{
		m_setList.Unlink( m_freeSetsList, node );
	}
	else
	{
		node = m_setList.Alloc();
		m_setList[node].queryList = m_queryList.CreateList();
	}
	m_setList.LinkToHead( m_activeSetsList, node );
	return node;
}
CPixelVisSet *CPixelVisibilitySystem::FindOrCreatePixelVisSet( const pixelvis_queryparams_t &params, pixelvis_handle_t *queryHandle )
{
	if ( queryHandle[0] )
	{
		unsigned short handle = queryHandle[0] & 0xFFFF;
		handle--;
		unsigned short serial = queryHandle[0] >> 16;
		if ( m_setList.IsValidIndex(handle) && m_setList[handle].serial == serial )
		{
			return &m_setList[handle];
		}
	}

	unsigned short node = AllocSet();
	m_setList[node].Init( params );
	unsigned int out = m_setList[node].serial;
	unsigned short nodeHandle = node + 1;
	out <<= 16;
	out |= nodeHandle;
	queryHandle[0] = out;
	return &m_setList[node];
}
void CPixelVisibilitySystem::FreeSet( unsigned short node )
{
	m_setList.Unlink( m_activeSetsList, node );
	m_setList.LinkToHead( m_freeSetsList, node );
	m_setList[node].serial++;
}