Files
GTASource/game/renderer/OcclusionQueries.cpp
T
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

654 lines
15 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// FILE : renderer/OcclusionQueries.cpp
// PURPOSE : per Entity Occlusion Query handling.
// AUTHOR : Alex H.
//
/////////////////////////////////////////////////////////////////////////////////
#include "grcore/allocscope.h"
#include "grcore/debugdraw.h"
#include "renderer/OcclusionQueries.h"
#include "renderer/Deferred/DeferredConfig.h"
#include "renderer/Sprite2d.h"
#include "system/FindSize.h"
atRangeArray<OcclusionQueries::OcclusionQuery,MAX_OCCLUSION_QUERIES> OcclusionQueries::s_OcclusionQueryArray;
atFreeList<u8,MAX_OCCLUSION_QUERIES> OcclusionQueries::s_OcclusionQueryFreeList;
static int s_OQAllocatedQueries;
atRangeArray<OcclusionQueries::ConditionalQuery,MAX_CONDITIONAL_QUERIES> OcclusionQueries::s_ConditionalQueryArray;
atFreeList<u8,MAX_CONDITIONAL_QUERIES> OcclusionQueries::s_ConditionalQueryFreeList;
static int s_CQAllocatedQueries;
int OcclusionQueries::s_BufferIdx;
grcRasterizerStateHandle OcclusionQueries::s_OcclusionRS;
grcDepthStencilStateHandle OcclusionQueries::s_OcclusionDS;
grcBlendStateHandle OcclusionQueries::s_OcclusionBS;
#if __BANK
static bool s_OQrenderOpaque = false;
static bool s_OQDebugRender = false;
static bool s_CQrenderOpaque = false;
#endif // __BANK
struct BoxQuery
{
Mat34V mtx;
union {
grcOcclusionQuery OQuery;
grcConditionalQuery CQuery;
};
} ;
//
// Occlusion Queries
//
OcclusionQueries::dlCmdBoxOcclusionQueries::dlCmdBoxOcclusionQueries()
{
maxQueries = s_OQAllocatedQueries;
Assert(maxQueries);
queries = gDCBuffer->AllocateObjectFromPagedMemory<BoxQuery>(DPT_LIFETIME_RENDER_THREAD, sizeof(BoxQuery) * maxQueries);
BoxQuery* pDest = queries;
Assert(pDest);
int bufferIdx = GetRenderBufferIdx();
numQueries = 0;
for(int i=0;i<s_OcclusionQueryArray.GetMaxCount();i++)
{
OcclusionQuery &query = s_OcclusionQueryArray[i];
if( query.GetIsAllocated() )
{
pDest->mtx = query.mtx;
pDest->OQuery = query.m_occlusionQueries[bufferIdx];
query.m_queryKicked[bufferIdx] = true;
Assert(pDest->OQuery);
pDest++;
numQueries++;
}
}
Assert(numQueries<=maxQueries);
}
void OcclusionQueries::dlCmdBoxOcclusionQueries::Execute()
{
GRC_ALLOC_SCOPE_AUTO_PUSH_POP()
BoxQuery* matrices = queries;
if( numQueries > 0 )
{
grcRasterizerStateHandle currentRS = grcStateBlock::RS_Active;
grcDepthStencilStateHandle currentDSS = grcStateBlock::DSS_Active;
grcBlendStateHandle currentBS = grcStateBlock::BS_Active;
#if __BANK
grcStateBlock::SetStates(GetRSBlock(), GetDSBlock(), s_OQrenderOpaque ? grcStateBlock::BS_Default : GetBSBLock());
#else // __BANK
grcStateBlock::SetStates(GetRSBlock(), GetDSBlock(), GetBSBLock());
#endif // __BANK
for(int i=0;i<numQueries;i++)
{
const Vec3V a = matrices->mtx.a();
const Vec3V b = matrices->mtx.b();
const Vec3V c = matrices->mtx.c();
const Vec3V d = matrices->mtx.d();
const Vec3V size = Vec3V(a.GetW(),b.GetW(),c.GetW());
Mat34V mtx(a,b,c,d);
mtx.SetM30M31M32Zero();
grcOcclusionQuery query = matrices->OQuery;
Assert(query);
GRCDEVICE.BeginOcclusionQuery(query);
grcDrawSolidBox(VEC3V_TO_VECTOR3(size), MAT34V_TO_MATRIX34(mtx), Color32(0xFFFFFFFF));
GRCDEVICE.EndOcclusionQuery(query);
matrices++;
}
grcWorldIdentity();
grcStateBlock::SetStates(currentRS, currentDSS, currentBS);
}
}
void OcclusionQueries::OcclusionQuery::Init()
{
mtx = Mat34V(V_ZERO);
pixelCount = -1;
for(int i=0;i<m_queryKicked.GetMaxCount();i++)
{
m_queryKicked[i] = false;
}
SetIsAllocated(true);
}
void OcclusionQueries::OcclusionQuery::Shutdown()
{
SetIsAllocated(false);
}
void OcclusionQueries::OcclusionQuery::SetBoundingBox(Vec3V_In min, Vec3V_In max, Mat34V_In matrix)
{
const Vec3V size = max - min;
const Vec3V center = (max + min) * ScalarV(V_HALF);
const Vec3V worldcenter = Transform(matrix,center);
Vec3V a = matrix.a();
Vec3V b = matrix.b();
Vec3V c = matrix.c();
Vec3V d = worldcenter;
a.SetW(size.GetX());
b.SetW(size.GetY());
c.SetW(size.GetZ());
d.SetW(mtx.d().GetW());
const Mat34V temp(a,b,c,d);
mtx = temp;
}
void OcclusionQueries::OcclusionQuery::SetIsAllocated(bool b)
{
if( b )
flags |= OQ_Allocated;
else
flags &= ~OQ_Allocated;
}
bool OcclusionQueries::OcclusionQuery::GetIsAllocated() const
{
return (flags & OQ_Allocated) == OQ_Allocated;
}
Mat34V_Out OcclusionQueries::OcclusionQuery::GetMatrix() const
{
return mtx;
}
Vec3V_Out OcclusionQueries::OcclusionQuery::GetSize() const
{
const Vec3V a = mtx.a();
const Vec3V b = mtx.b();
const Vec3V c = mtx.c();
const Vec3V res(a.GetW(),b.GetW(),c.GetW());
return res;
}
//
// Conditional Queries
//
OcclusionQueries::dlCmdBoxConditionalQueries::dlCmdBoxConditionalQueries()
{
maxQueries = s_CQAllocatedQueries;
Assert(maxQueries);
queries = gDCBuffer->AllocateObjectFromPagedMemory<BoxQuery>(DPT_LIFETIME_RENDER_THREAD, sizeof(BoxQuery) * maxQueries);
BoxQuery* pDest = queries;
Assert(pDest);
numQueries = 0;
for(int i=0;i<s_ConditionalQueryArray.GetMaxCount();i++)
{
ConditionalQuery &query = s_ConditionalQueryArray[i];
if( query.GetIsAllocated() )
{
pDest->mtx = query.mtx;
pDest->CQuery = query.m_conditionalQuery;
Assert(pDest->CQuery);
pDest++;
numQueries++;
}
}
Assert(numQueries<=maxQueries);
}
void OcclusionQueries::dlCmdBoxConditionalQueries::Execute()
{
GRC_ALLOC_SCOPE_AUTO_PUSH_POP()
BoxQuery* matrices = queries;
if( numQueries > 0 )
{
grcRasterizerStateHandle currentRS = grcStateBlock::RS_Active;
grcDepthStencilStateHandle currentDSS = grcStateBlock::DSS_Active;
grcBlendStateHandle currentBS = grcStateBlock::BS_Active;
Color32 boxColor(0x00FFFFFF);
#if __BANK
if (s_OQrenderOpaque)
{
grcStateBlock::SetStates(GetRSBlock(), GetDSBlock(), grcStateBlock::BS_Default);
boxColor.Set(0xFFFFFFFF);
}
else
{
grcStateBlock::SetStates(GetRSBlock(), GetDSBlock(), GetBSBLock());
}
#else // __BANK
grcStateBlock::SetStates(GetRSBlock(), GetDSBlock(), GetBSBLock());
#endif // __BANK
for(int i=0;i<numQueries;i++)
{
const Vec3V a = matrices->mtx.a();
const Vec3V b = matrices->mtx.b();
const Vec3V c = matrices->mtx.c();
const Vec3V d = matrices->mtx.d();
const Vec3V size = Vec3V(a.GetW(),b.GetW(),c.GetW());
Mat34V mtx(a,b,c,d);
mtx.SetM30M31M32Zero();
grcConditionalQuery query = matrices->CQuery;
Assert(query);
GRCDEVICE.BeginConditionalQuery(query);
grcDrawSolidBox(VEC3V_TO_VECTOR3(size), MAT34V_TO_MATRIX34(mtx), boxColor);
GRCDEVICE.EndConditionalQuery(query);
matrices++;
}
grcWorldIdentity();
grcStateBlock::SetStates(currentRS, currentDSS, currentBS);
}
}
void OcclusionQueries::ConditionalQuery::Init()
{
mtx = Mat34V(V_ZERO);
SetIsAllocated(true);
}
void OcclusionQueries::ConditionalQuery::Shutdown()
{
SetIsAllocated(false);
}
void OcclusionQueries::ConditionalQuery::SetBoundingBox(Vec3V_In min, Vec3V_In max, Mat34V_In matrix)
{
const Vec3V size = max - min;
const Vec3V center = (max + min) * ScalarV(V_HALF);
const Vec3V worldcenter = Transform(matrix,center);
Vec3V a = matrix.a();
Vec3V b = matrix.b();
Vec3V c = matrix.c();
Vec3V d = worldcenter;
a.SetW(size.GetX());
b.SetW(size.GetY());
c.SetW(size.GetZ());
d.SetW(mtx.d().GetW());
const Mat34V temp(a,b,c,d);
mtx = temp;
}
void OcclusionQueries::ConditionalQuery::SetIsAllocated(bool b)
{
if( b )
flags |= CQ_Allocated;
else
flags &= ~CQ_Allocated;
}
bool OcclusionQueries::ConditionalQuery::GetIsAllocated() const
{
return (flags & CQ_Allocated) == CQ_Allocated;
}
Mat34V_Out OcclusionQueries::ConditionalQuery::GetMatrix() const
{
return mtx;
}
Vec3V_Out OcclusionQueries::ConditionalQuery::GetSize() const
{
const Vec3V a = mtx.a();
const Vec3V b = mtx.b();
const Vec3V c = mtx.c();
const Vec3V res(a.GetW(),b.GetW(),c.GetW());
return res;
}
void OcclusionQueries::Init()
{
s_OcclusionQueryFreeList.MakeAllFree();
s_ConditionalQueryFreeList.MakeAllFree();
s_OQAllocatedQueries = 0;
s_CQAllocatedQueries = 0;
s_BufferIdx = 0;
for(int i=0;i<s_OcclusionQueryArray.GetMaxCount();i++)
{
s_OcclusionQueryArray[i].Init();
s_OcclusionQueryArray[i].SetIsAllocated(false);
}
for(int i=0;i<s_ConditionalQueryArray.GetMaxCount();i++)
{
s_ConditionalQueryArray[i].Init();
s_ConditionalQueryArray[i].SetIsAllocated(false);
}
InitHWQueries();
grcRasterizerStateDesc RSDesc;
RSDesc.CullMode=grcRSV::CULL_NONE;
s_OcclusionRS = grcStateBlock::CreateRasterizerState(RSDesc);
grcDepthStencilStateDesc DSDesc;
DSDesc.DepthEnable = true;
DSDesc.DepthWriteMask = false;
DSDesc.DepthFunc = rage::FixupDepthDirection(grcRSV::CMP_LESSEQUAL);
s_OcclusionDS = grcStateBlock::CreateDepthStencilState(DSDesc);
grcBlendStateDesc BSDesc;
BSDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
s_OcclusionBS = grcStateBlock::CreateBlendState(BSDesc);
DLC_REGISTER_EXTERNAL(dlCmdBoxOcclusionQueries);
DLC_REGISTER_EXTERNAL(dlCmdBoxConditionalQueries);
}
#if __BANK
void OcclusionQueries::AddWidgets(bkBank &bk)
{
bk.PushGroup("Visibility Queries", false);
bk.AddToggle("Render opaque occlusion queries",&s_OQrenderOpaque);
bk.AddToggle("Render opaque conditional queries",&s_CQrenderOpaque);
bk.AddToggle("Render debug info",&s_OQDebugRender);
bk.PopGroup();
}
#endif // __BANK
void OcclusionQueries::InitHWQueries()
{
for(int i=0;i<s_OcclusionQueryArray.GetMaxCount();i++)
{
OcclusionQuery& query = s_OcclusionQueryArray[i];
for(int j=0;j<query.m_occlusionQueries.GetMaxCount();j++)
{
query.m_occlusionQueries[j] = GRCDEVICE.CreateOcclusionQuery();
Assert(query.m_occlusionQueries[j]);
}
}
for(int i=0;i<s_ConditionalQueryArray.GetMaxCount();i++)
{
ConditionalQuery& query = s_ConditionalQueryArray[i];
query.m_conditionalQuery = GRCDEVICE.CreateConditionalQuery();
}
}
void OcclusionQueries::Shutdown()
{
for(int i=0;i<s_OcclusionQueryArray.GetMaxCount();i++)
{
if( s_OcclusionQueryArray[i].GetIsAllocated() )
{
s_OcclusionQueryArray[i].Shutdown();
OcclusionQuery& query = s_OcclusionQueryArray[i];
for(int j=0;j<query.m_occlusionQueries.GetMaxCount();j++)
{
if( query.m_occlusionQueries[i] )
GRCDEVICE.DestroyOcclusionQuery(query.m_occlusionQueries[i]);
}
}
}
for(int i=0;i<s_ConditionalQueryArray.GetMaxCount();i++)
{
if( s_ConditionalQueryArray[i].GetIsAllocated() )
{
s_ConditionalQueryArray[i].Shutdown();
ConditionalQuery& query = s_ConditionalQueryArray[i];
if( query.m_conditionalQuery )
{
GRCDEVICE.DestroyConditionalQuery(query.m_conditionalQuery);
}
}
}
}
unsigned int OcclusionQueries::OQAllocate()
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
int newId = s_OcclusionQueryFreeList.Allocate();
if( newId > -1 )
{
s_OcclusionQueryArray[newId].Init();
s_OQAllocatedQueries++;
}
return newId + 1;
}
void OcclusionQueries::OQFree(unsigned int idx)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
Assert(idx != 0);
Assert(s_OcclusionQueryArray[idx - 1].GetIsAllocated());
s_OcclusionQueryArray[idx - 1].Shutdown();
s_OcclusionQueryFreeList.Free(idx - 1);
s_OQAllocatedQueries--;
}
void OcclusionQueries::OQSetBoundingBox(unsigned int idx,Vec3V_In min, Vec3V_In max, Mat34V_In matrix)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
Assert(idx != 0);
Assert(s_OcclusionQueryArray[idx - 1].GetIsAllocated());
s_OcclusionQueryArray[idx - 1].SetBoundingBox(min,max,matrix);
}
grcOcclusionQuery OcclusionQueries::OQGetQuery(unsigned int idx)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
Assert(idx != 0);
Assert(s_OcclusionQueryArray[idx - 1].GetIsAllocated());
int bufferIdx = GetRenderBufferIdx();
OcclusionQuery& query = s_OcclusionQueryArray[idx - 1];
return query.m_occlusionQueries[bufferIdx];
}
int OcclusionQueries::OQGetQueryResult(unsigned int idx)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
Assert(idx != 0);
Assert(s_OcclusionQueryArray[idx - 1].GetIsAllocated());
return s_OcclusionQueryArray[idx - 1].pixelCount;
}
unsigned int OcclusionQueries::CQAllocate()
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
int newId = s_ConditionalQueryFreeList.Allocate();
if( newId > -1 )
{
s_ConditionalQueryArray[newId].Init();
s_CQAllocatedQueries++;
}
return newId + 1;
}
void OcclusionQueries::CQFree(unsigned int idx)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
Assert(idx != 0);
Assert(s_ConditionalQueryArray[idx - 1].GetIsAllocated());
s_ConditionalQueryArray[idx - 1].Shutdown();
s_ConditionalQueryFreeList.Free(idx - 1);
s_CQAllocatedQueries--;
}
void OcclusionQueries::CQSetBoundingBox(unsigned int idx,Vec3V_In min, Vec3V_In max, Mat34V_In matrix)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
Assert(idx != 0);
Assert(s_ConditionalQueryArray[idx - 1].GetIsAllocated());
s_ConditionalQueryArray[idx - 1].SetBoundingBox(min,max,matrix);
}
grcConditionalQuery OcclusionQueries::CQGetQuery(unsigned int idx)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
Assert(idx != 0);
Assert(s_ConditionalQueryArray[idx - 1].GetIsAllocated());
ConditionalQuery& query = s_ConditionalQueryArray[idx - 1];
return query.m_conditionalQuery;
}
void OcclusionQueries::RenderBoxBasedQueries()
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
int OQueries = s_OQAllocatedQueries;
if( OQueries )
{
DLC(OcclusionQueries::dlCmdBoxOcclusionQueries,());
}
int CQueries = s_CQAllocatedQueries;
if( CQueries )
{
DLC(OcclusionQueries::dlCmdBoxConditionalQueries,());
}
}
void OcclusionQueries::GatherQueryResults()
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
int bufferIdx = GetUpdateBufferIdx();
for(int i=0;i<s_OcclusionQueryArray.GetMaxCount();i++)
{
OcclusionQuery &query = s_OcclusionQueryArray[i];
if( query.GetIsAllocated() )
{
grcOcclusionQuery readQuery = query.m_occlusionQueries[bufferIdx];
bool wasKicked = query.m_queryKicked[bufferIdx];
int result = -1;
u32 drawCount = 0;
#if __BANK
bool HWFailure = false;
#endif // __BANK
if( readQuery && wasKicked)
{
if( GRCDEVICE.GetOcclusionQueryData(readQuery,drawCount) )
{
result = (int)drawCount;
}
#if __BANK
else
{
HWFailure = true;
}
#endif // __BANK
}
query.pixelCount = result;
#if __BANK
if( s_OQDebugRender )
{
const Vec3V d = query.mtx.d();
char str[128];
sprintf(str,"%d",query.pixelCount);
Color32 col = query.pixelCount == -1 ? Color32(0xff,0x00,0x00) : Color32(0x00,0xff,0x00);
col = HWFailure == true ? Color32(0x00,0x00,0xff) : col;
grcDebugDraw::Text(d, col, str, false);
}
#endif // __BANK
}
}
FlipBufferIdx();
}
int OcclusionQueries::GetRenderBufferIdx()
{
return s_BufferIdx;
}
int OcclusionQueries::GetUpdateBufferIdx()
{
return (s_BufferIdx+1) % MAX_NUM_BUFFERED_OCCLUSION_QUERIES;
}
void OcclusionQueries::FlipBufferIdx()
{
s_BufferIdx = (s_BufferIdx+1) % MAX_NUM_BUFFERED_OCCLUSION_QUERIES;
}