Files
GTASource/game/ai/ExpensiveProcess.cpp

335 lines
13 KiB
C++
Raw Permalink Normal View History

2025-02-23 17:40:52 +08:00
//
// ai/ExpensiveProcess.cpp
//
// Copyright (C) 1999-2010 Rockstar Games. All Rights Reserved.
//
// File header
#include "ai/ExpensiveProcess.h"
// Rage headers
#include "bank/bank.h"
#include "profile/group.h"
#include "profile/page.h"
// Game headers
#include "Peds/PedDefines.h"
#include "Vehicles/VehicleDefines.h"
AI_OPTIMISATIONS()
// Performance monitors
PF_PAGE(AIDistributedProcesses, "AI Distributed Processes");
PF_GROUP(ProcessCost);
PF_LINK(AIDistributedProcesses, ProcessCost);
PF_TIMER(RedistributionCost, ProcessCost);
PF_TIMER(OverallCost, ProcessCost);
PF_TIMER(EntityScanner, ProcessCost);
PF_TIMER(AcquaintanceScanner, ProcessCost);
PF_TIMER(EncroachmentScanner, ProcessCost);
PF_TIMER(GroupScanner, ProcessCost);
PF_TIMER(VehicleCollisionScanner, ProcessCost);
PF_TIMER(PotentialCollisionScanner, ProcessCost);
PF_TIMER(FireScanner, ProcessCost);
PF_TIMER(PassengerEventScanner, ProcessCost);
PF_TIMER(TargettingLos, ProcessCost);
PF_TIMER(ShockingEventScanner, ProcessCost);
////////////////////////////////////////////////////////////////////////////////
CExpensiveProcess::CExpensiveProcess(ProcessType processType)
: m_ProcessType(processType)
, m_iAllocatedInterval(-1)
, m_bRegistered(false)
, m_bProcessedOnceInCurrentSlot(false)
, m_IndexInProcessArray(INVALID_EXPENSIVE_PROCESS_ARRAY_INDEX)
{
Assert((int)processType == (int)m_ProcessType); // Make sure it fit.
CompileTimeAssert(CExpensiveProcess::MAX_PROCESS_TYPES < (1 << CExpensiveProcess::kBitsForProcessType));
}
////////////////////////////////////////////////////////////////////////////////
CExpensiveProcess::~CExpensiveProcess()
{
if(IsRegistered())
{
UnregisterSlot();
}
}
////////////////////////////////////////////////////////////////////////////////
// Static initialisation
CExpensiveProcessDistributer::ProcessArray CExpensiveProcessDistributer::ms_aProcesses[CExpensiveProcess::MAX_PROCESS_TYPES];
u16 CExpensiveProcessDistributer::ms_aProcessesCounts[CExpensiveProcess::MAX_PROCESS_TYPES];
u16 CExpensiveProcessDistributer::ms_iCurrentIntervals[CExpensiveProcess::MAX_PROCESS_TYPES];
u8 CExpensiveProcessDistributer::ms_iCurrentPhases[CExpensiveProcess::MAX_PROCESS_TYPES];
CExpensiveProcessDistributer::ProcessArray CExpensiveProcessDistributer::ms_aProcessesToSort;
s32 CExpensiveProcessDistributer::ms_iLastSortedType = 0;
CExpensiveProcessDistributer::CProcessInfo CExpensiveProcessDistributer::ms_StaticProcessInfo[CExpensiveProcess::MAX_PROCESS_TYPES] =
{
CProcessInfo(15, "PPT_PedScanner"),
CProcessInfo(15, "PPT_VehicleScanner"),
CProcessInfo(8, "PPT_DoorScanner"),
CProcessInfo(2, "PPT_AcquaintanceScanner"),
CProcessInfo(10, "PPT_EncroachmentScanner"),
CProcessInfo(10, "PPT_GroupScanner"),
CProcessInfo(15, "PPT_VehicleCollisionScanner"),
CProcessInfo(15, "PPT_ObjectCollisionScanner"),
CProcessInfo(3, "PPT_PotentialCollisionScanner"),
CProcessInfo(15, "PPT_FireScanner"),
CProcessInfo(15, "PPT_DriverEventScanner"),
CProcessInfo(2, 4, "PPT_TargettingLos"),
CProcessInfo(10, 8, "PPT_ShockingEventScanner"),
CProcessInfo(60, "PPT_AgitationScanner"),
CProcessInfo(3, 3, "PPT_CoverFinderDistributer"),
CProcessInfo(60, 1, "PPT_AudioDistributer"),
CProcessInfo(2, 4, "PPT_CombatTransitionDistributer"),
CProcessInfo(1, 60, "PPT_ScenarioExitDistributer"),
CProcessInfo(15, "VPT_PedScanner"),
CProcessInfo(15, "VPT_VehicleScanner"),
CProcessInfo(15, "VPT_ObjectScanner"),
CProcessInfo(4, "VPT_SteeringDistributer"),
CProcessInfo(4, "VPT_BrakingDistributer"),
CProcessInfo(4, "VPT_LaneChangeDistributer"),
CProcessInfo(2, "VPT_DummyAvoidanceDistributer"),
CProcessInfo(15, "VPT_TailgateDistributer"),
CProcessInfo(10, "VPT_NavmeshLOSDistributer"),
CProcessInfo(2, "VPT_UpdateJunctionsDistributer"),
CProcessInfo(7, "VPT_SirenReactionDistributer"),
CProcessInfo(60, "VPT_AdverseConditionsDistributer"),
CProcessInfo(30, "VPT_HeliAvoidanceDistributer")
};
////////////////////////////////////////////////////////////////////////////////
void CExpensiveProcessDistributer::Init(u32 UNUSED_PARAM(uInitMode))
{
ms_StaticProcessInfo[CExpensiveProcess::PPT_AudioDistributer].m_iMaxFramesBetweenUpdatesForIrregularUse =
ms_StaticProcessInfo[CExpensiveProcess::PPT_AudioDistributer].m_iDesiredInterval * 2;
// Timesliced vehicle updates may involve calls to a number of CExpensiveProcess objects, and
// if we are not careful, the timesliced updates are unlikely to coincide with the CExpensiveProcess intervals.
// For this reason, we make use of the new m_iMaxFramesBetweenUpdatesForIrregularUse variable to make sure
// we still get to update these processes during the timesliced AI updates. In most cases, just setting the
// irregular interval to the same as the desired regular interval is probably fine.
ms_StaticProcessInfo[CExpensiveProcess::VPT_SteeringDistributer].m_iMaxFramesBetweenUpdatesForIrregularUse = ms_StaticProcessInfo[CExpensiveProcess::VPT_SteeringDistributer].m_iDesiredInterval;
ms_StaticProcessInfo[CExpensiveProcess::VPT_BrakingDistributer].m_iMaxFramesBetweenUpdatesForIrregularUse = ms_StaticProcessInfo[CExpensiveProcess::VPT_BrakingDistributer].m_iDesiredInterval;
ms_StaticProcessInfo[CExpensiveProcess::VPT_DummyAvoidanceDistributer].m_iMaxFramesBetweenUpdatesForIrregularUse = ms_StaticProcessInfo[CExpensiveProcess::VPT_DummyAvoidanceDistributer].m_iDesiredInterval;
ms_StaticProcessInfo[CExpensiveProcess::VPT_LaneChangeDistributer].m_iMaxFramesBetweenUpdatesForIrregularUse = ms_StaticProcessInfo[CExpensiveProcess::VPT_LaneChangeDistributer].m_iDesiredInterval;
ms_StaticProcessInfo[CExpensiveProcess::VPT_UpdateJunctionsDistributer].m_iMaxFramesBetweenUpdatesForIrregularUse = ms_StaticProcessInfo[CExpensiveProcess::VPT_UpdateJunctionsDistributer].m_iDesiredInterval;
ms_StaticProcessInfo[CExpensiveProcess::VPT_VehicleScanner].m_iMaxFramesBetweenUpdatesForIrregularUse = 20; // Can perhaps go a bit slower than the normal 15 frames for this one?
ms_StaticProcessInfo[CExpensiveProcess::VPT_ObjectScanner].m_iMaxFramesBetweenUpdatesForIrregularUse = 20; // Can perhaps go a bit slower than the normal 15 frames for this one?
ms_StaticProcessInfo[CExpensiveProcess::VPT_AdverseConditionsDistributer].m_iMaxFramesBetweenUpdatesForIrregularUse = ms_StaticProcessInfo[CExpensiveProcess::VPT_AdverseConditionsDistributer].m_iDesiredInterval;
// TODO: Think more about if we want this for other expensive processes used during timesliced updates,
// such as m_TailgateDistributer, VPT_AdverseConditionsDistributer, VPT_PedScanner, and VPT_ObjectScanner.
// So far we haven't come across any situations where these seem necessary.
for(s32 i = CExpensiveProcess::FIRST_PED_PROCESS; i < CExpensiveProcess::MAX_PED_PROCESSES; i++)
{
ms_aProcesses[i].Reserve(MAXNOOFPEDS);
ms_aProcesses[i].Resize(MAXNOOFPEDS);
}
for(s32 i = CExpensiveProcess::FIRST_VEHICLE_PROCESS; i < CExpensiveProcess::MAX_VEHICLE_PROCESSES; i++)
{
ms_aProcesses[i].Reserve(VEHICLE_POOL_SIZE);
ms_aProcesses[i].Resize(VEHICLE_POOL_SIZE);
}
// NULL the arrays
for(s32 i = 0; i < CExpensiveProcess::MAX_PROCESS_TYPES; i++)
{
for(s32 j = 0; j < ms_aProcesses[i].GetCount(); j++)
{
ms_aProcesses[i][j] = NULL;
}
// If this fails, m_iMaxFramesBetweenUpdatesForIrregularUse has been set to a higher value than we can expect
// to work properly. We use a very limited number of bits for the frame count so if anybody waits for too long
// between irregular updates, the frame count will have wrapped around. For example, if 8 bits are used and you set
// m_iMaxFramesBetweenUpdatesForIrregularUse to some value close to 1<<8, like 253, you would have a very small window
// within which the irregular update would have to occur and it probably wouldn't work right.
Assert(ms_StaticProcessInfo[i].m_iMaxFramesBetweenUpdatesForIrregularUse <= CExpensiveProcess::GetMaxRecommendedFramesBetweenIrregularUpdates());
}
for(s32 i = 0; i < CExpensiveProcess::MAX_PROCESS_TYPES; i++)
{
ms_aProcessesCounts[i] = 0;
ms_iCurrentIntervals[i] = 0;
ms_iCurrentPhases[i] = 0;
}
s32 iMaxListSize = Max(MAXNOOFPEDS, VEHICLE_POOL_SIZE);
ms_aProcessesToSort.Reserve(iMaxListSize);
}
////////////////////////////////////////////////////////////////////////////////
void CExpensiveProcessDistributer::Update()
{
PF_FUNC(RedistributionCost);
AdvanceCurrentInterval();
AdvanceSortType();
const int type = ms_iLastSortedType;
s8 iMaxProcessesPerFrame = ms_StaticProcessInfo[type].m_iMaxProcessPerFrame;
if( iMaxProcessesPerFrame > 0 )
{
unsigned int iInterval = ms_StaticProcessInfo[type].m_iOriginalDesiredInterval;
const unsigned int iCurrentProcessCount = ms_aProcessesCounts[type];
while((unsigned int)iMaxProcessesPerFrame < iCurrentProcessCount/iInterval)
{
++iInterval;
}
Assert(iInterval <= 0xff);
ms_StaticProcessInfo[type].m_iDesiredInterval = (u8)iInterval;
unsigned int phase = (ms_iCurrentIntervals[type] % iInterval);
aiAssert(phase <= 0xff);
ms_iCurrentPhases[type] = (u8)phase;
}
// Resort the current list
ms_aProcessesToSort.Resize(0);
// Cache the process array
ProcessArray& aProcesses = ms_aProcesses[type];
// Find all movable processes
for(s32 i = 0; i < aProcesses.GetCount(); i++)
{
if(aProcesses[i] && aProcesses[i]->GetHasBeenProcessedInCurrentSlot())
{
ms_aProcessesToSort.Push(aProcesses[i]);
aProcesses[i] = NULL;
--ms_aProcessesCounts[type];
}
}
// Re-add all processes
for(s32 i = 0; i < ms_aProcessesToSort.GetCount(); i++)
{
AddProcess(ms_aProcessesToSort[i]);
}
}
////////////////////////////////////////////////////////////////////////////////
#if __BANK
void CExpensiveProcessDistributer::AddWidgets(bkBank& bank)
{
bank.PushGroup("Process Distribution", false);
for(s32 i = 0; i < CExpensiveProcess::MAX_PROCESS_TYPES; i++)
{
char buff[128];
#if __ASSERT
formatf(buff, 128, "%s interval", ms_StaticProcessInfo[i].m_szName);
#else
formatf(buff, 128, "%d interval", i);
#endif // __ASSERT
bank.AddSlider(buff, &ms_StaticProcessInfo[i].m_iDesiredInterval, 0, 60, 1);
}
bank.PopGroup(); // "ProcessDistribution"
}
#endif // __BANK
////////////////////////////////////////////////////////////////////////////////
bool CExpensiveProcessDistributer::AddProcess(CExpensiveProcess* pProcess)
{
aiAssert(pProcess);
// Cache the process array
const CExpensiveProcess::ProcessType processType = pProcess->GetType();
ProcessArray& aProcesses = ms_aProcesses[processType];
// Find a free slot for the process
for(s32 i = 0; i < aProcesses.GetCount(); i++)
{
if(aProcesses[i] == NULL)
{
aProcesses[i] = pProcess;
pProcess->SetHasBeenProcessedInCurrentSlot(false);
pProcess->SetInterval(i % ms_StaticProcessInfo[processType].m_iDesiredInterval);
Assert(ms_aProcessesCounts[processType] < 0xffff);
++ms_aProcessesCounts[processType];
Assert(i < INVALID_EXPENSIVE_PROCESS_ARRAY_INDEX);
pProcess->SetIndexInProcessArray(i);
return true;
}
}
aiAssertf(0, "Out of slots for process type [%s]", ms_StaticProcessInfo[pProcess->GetType()].m_szName);
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool CExpensiveProcessDistributer::RemoveProcess(CExpensiveProcess* pProcess)
{
aiAssert(pProcess);
// Cache the process array
const CExpensiveProcess::ProcessType type = pProcess->GetType();
ProcessArray& aProcesses = ms_aProcesses[type];
u32 index = pProcess->GetIndexInProcessArray();
Assert(aProcesses[index] == pProcess);
pProcess->SetIndexInProcessArray(INVALID_EXPENSIVE_PROCESS_ARRAY_INDEX);
aProcesses[index] = NULL;
--ms_aProcessesCounts[type];
return true;
}
////////////////////////////////////////////////////////////////////////////////
void CExpensiveProcessDistributer::AdvanceCurrentInterval()
{
for(s32 i = 0; i < CExpensiveProcess::MAX_PROCESS_TYPES; i++)
{
unsigned int newInterval = ms_iCurrentIntervals[i] + 1;
unsigned int newPhase;
if(newInterval >= ms_aProcesses[i].GetCount())
{
newInterval = newPhase = 0;
}
else
{
newPhase = ms_iCurrentPhases[i] + 1;
if(newPhase >= ms_StaticProcessInfo[i].m_iDesiredInterval)
{
newPhase = 0;
}
}
aiAssert(newInterval <= 0xffff);
aiAssert(newPhase <= 0xff);
aiAssertf(newPhase == (newInterval % ms_StaticProcessInfo[i].m_iDesiredInterval), "%s: newPhase - %i; newInterval - %i, DesiredInterval - %i", ms_StaticProcessInfo[i].m_szName, newPhase, newInterval, ms_StaticProcessInfo[i].m_iDesiredInterval);
ms_iCurrentIntervals[i] = (u16)newInterval;
ms_iCurrentPhases[i] = (u8)newPhase;
}
}
////////////////////////////////////////////////////////////////////////////////
void CExpensiveProcessDistributer::AdvanceSortType()
{
int newType = ms_iLastSortedType + 1;
if(newType >= CExpensiveProcess::MAX_PROCESS_TYPES)
{
newType = 0;
}
ms_iLastSortedType = newType;
}