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

2601 lines
80 KiB
C++

// File header
#include "streaming/PrioritizedClipSetStreamer.h"
// Rage headers
#include "fwanimation/animmanager.h"
#include "fwanimation/clipsets.h"
#include "fwsys/timer.h"
#include "game/GameSituation.h"
#include "grcore/debugdraw.h"
#include "streaming/streaming.h"
#include "streaming/streamingdefs.h"
#include "streaming/streamingvisualize.h"
// Game headers
#include "task/system/taskhelpers.h"
STREAMING_OPTIMISATIONS()
AI_VEHICLE_OPTIMISATIONS()
//----------------------------------------------------------------------
RAGE_DEFINE_SUBCHANNEL(streaming, prioritized);
//----------------------------------------------------------------------
namespace
{
const atHashWithStringNotFinal s_hDefaultMemoryGroup = "MG_None";
const eStreamingPriority s_nDefaultStreamingPriority = SP_Medium;
}
////////////////////////////////////////////////////////////////////////////////
// CPrioritizedClipSetRequest
////////////////////////////////////////////////////////////////////////////////
FW_INSTANTIATE_CLASS_POOL(CPrioritizedClipSetRequest, CONFIGURED_FROM_FILE, atHashString("CPrioritizedClipSetRequest",0x22b81e14));
CPrioritizedClipSetRequest::CPrioritizedClipSetRequest(fwMvClipSetId clipSetId)
: fwRefAwareBase()
, m_ClipSet(NULL)
, m_ClipSetId(clipSetId)
, m_nStreamingPriority(SP_Invalid)
, m_nStreamingPriorityOverride(SP_Invalid)
, m_nDesiredStreamingState(DSS_Unknown)
, m_uRunningFlags(0)
, m_uReferences(0)
, m_uTiebreaker(0)
{
if(streamPrioritizedVerifyf(m_ClipSetId != CLIP_SET_ID_INVALID, "The clip set is invalid."))
{
m_ClipSet = fwClipSetManager::GetClipSet(m_ClipSetId);
}
}
CPrioritizedClipSetRequest::~CPrioritizedClipSetRequest()
{
//Check if we have set the don't delete flag.
if(HasSetDontDeleteFlag())
{
//Clear the don't delete flag.
ClearDontDeleteFlag();
}
//Check if we have added a reference to the clip set.
if(HasAddedReferenceToClipSet())
{
//Release the clip set.
ReleaseClipSet();
}
}
int CPrioritizedClipSetRequest::GetClipDictionaryIndex() const
{
//Ensure the clip set is valid.
if(!streamPrioritizedVerifyf(m_ClipSet, "The clip set with id: %s is invalid.", m_ClipSetId.GetCStr()))
{
return false;
}
return m_ClipSet->GetClipDictionaryIndex().Get();
}
bool CPrioritizedClipSetRequest::IsClipSetStreamedIn() const
{
//Ensure the clip set is valid.
if(!streamPrioritizedVerifyf(m_ClipSet, "The clip set with id: %s is invalid.", m_ClipSetId.GetCStr()))
{
return false;
}
return IsClipSetStreamedIn(*m_ClipSet);
}
void CPrioritizedClipSetRequest::AddReferenceToClipSet(fwClipSet& rClipSet)
{
//Ensure that we have not added a reference to the clip set.
if(!streamPrioritizedVerifyf(!HasAddedReferenceToClipSet(), "A reference has already been added to clip set with id: %s.", m_ClipSetId.GetCStr()))
{
return;
}
//Set the flag.
m_uRunningFlags.SetFlag(RF_HasAddedReferenceToClipSet);
//Add a reference to the clip set.
rClipSet.AddRef_DEPRECATED();
streamPrioritizedDebugf3("Prioritized clip set: %s added a reference.", m_ClipSetId.TryGetCStr());
//Check if we have set the don't delete flag.
if(HasSetDontDeleteFlag())
{
//Clear the don't delete flag.
ClearDontDeleteFlag(rClipSet);
}
}
void CPrioritizedClipSetRequest::ClearDontDeleteFlag()
{
//Ensure the clip set is valid.
if(!streamPrioritizedVerifyf(m_ClipSet, "The clip set with id: %s is invalid.", m_ClipSetId.GetCStr()))
{
return;
}
//Clear the don't delete flag.
ClearDontDeleteFlag(*m_ClipSet);
}
void CPrioritizedClipSetRequest::ClearDontDeleteFlag(fwClipSet& rClipSet)
{
//Assert that we have set the flag.
streamPrioritizedAssert(HasSetDontDeleteFlag());
//Clear the flag.
rClipSet.ClearRequiredFlag_DEPRECATED(STRFLAG_DONTDELETE);
//Clear the flag.
m_uRunningFlags.ClearFlag(RF_HasSetDontDeleteFlag);
}
void CPrioritizedClipSetRequest::Initialize()
{
//Ensure the clip set is valid.
if(!streamPrioritizedVerifyf(m_ClipSet, "The clip set with id: %s is invalid.", m_ClipSetId.GetCStr()))
{
return;
}
//Ensure the clip dictionary metadata is valid.
fwClipDictionaryMetadata* pClipDictionaryMetadata = m_ClipSet->GetClipDictionaryMetadata();
streamPrioritizedAssertf(pClipDictionaryMetadata, "The metadata is invalid for clip dictionary: %s.", m_ClipSet->GetClipDictionaryName().GetCStr());
//Set the streaming priority.
m_nStreamingPriority = pClipDictionaryMetadata ? pClipDictionaryMetadata->GetStreamingPriority() : s_nDefaultStreamingPriority;
}
bool CPrioritizedClipSetRequest::IsClipSetStreamedIn(const fwClipSet& rClipSet) const
{
return rClipSet.IsStreamedIn_DEPRECATED();
}
void CPrioritizedClipSetRequest::ReleaseClipSet()
{
//Ensure the clip set is valid.
if(!streamPrioritizedVerifyf(m_ClipSet, "The clip set with id: %s is invalid.", m_ClipSetId.GetCStr()))
{
return;
}
//Release the clip set.
ReleaseClipSet(*m_ClipSet);
}
void CPrioritizedClipSetRequest::ReleaseClipSet(fwClipSet& rClipSet)
{
//Assert that we have added a reference to the clip set.
if(!streamPrioritizedVerifyf(HasAddedReferenceToClipSet(), "A reference has not been added to clip set with id: %s.", m_ClipSetId.GetCStr()))
{
return;
}
//Clear the flag.
m_uRunningFlags.ClearFlag(RF_HasAddedReferenceToClipSet);
//Release the clip set.
rClipSet.Release_DEPRECATED();
streamPrioritizedDebugf3("Prioritized clip set: %s was released.", m_ClipSetId.TryGetCStr());
}
bool CPrioritizedClipSetRequest::RequestClipSet(fwClipSet& rClipSet)
{
//Generate the flags.
s32 iFlags = STRFLAG_FORCE_LOAD | STRFLAG_DONTDELETE;
//Check if the clip set is high priority.
if(GetStreamingPriority() == SP_High)
{
iFlags |= STRFLAG_PRIORITY_LOAD;
}
//Note that we have set the don't delete flag.
m_uRunningFlags.SetFlag(RF_HasSetDontDeleteFlag);
//Request the clip set.
if(!rClipSet.Request_DEPRECATED(iFlags))
{
return false;
}
return true;
}
void CPrioritizedClipSetRequest::StreamOutClipSet(fwClipSet& rClipSet)
{
//Check if the clip dictionary index is valid.
int iClipDictionaryIndex = rClipSet.GetClipDictionaryIndex().Get();
if(streamPrioritizedVerifyf(iClipDictionaryIndex >= 0, "The clip dictionary index is invalid for clip set with id: %s.", m_ClipSetId.GetCStr()))
{
//Grab the streaming module id.
s32 iModuleId = fwAnimManager::GetStreamingModuleId();
//Grab the streaming index.
strStreamingModule* pStreamingModule = strStreamingEngine::GetInfo().GetModuleMgr().GetModule(iModuleId);
strIndex index = pStreamingModule->GetStreamingIndex(strLocalIndex(iClipDictionaryIndex));
//Check if the object is ready to be deleted.
if(strStreamingEngine::GetInfo().IsObjectReadyToDelete(index, 0))
{
//Remove the object.
strStreamingEngine::GetInfo().RemoveObject(index);
}
}
//Check if a fallback clip set exists.
fwClipSet* pFallbackClipSet = rClipSet.GetFallbackSet();
if(pFallbackClipSet)
{
//Stream out the fallback clip set.
StreamOutClipSet(*pFallbackClipSet);
}
}
void CPrioritizedClipSetRequest::Update(fwClipSet& rClipSet)
{
STRVIS_AUTO_CONTEXT(strStreamingVisualize::CLIPSETSTREAMER);
//Check if the clip set is not streamed in.
bool bIsClipSetStreamedIn = IsClipSetStreamedIn(rClipSet);
if(!bIsClipSetStreamedIn)
{
//Check if the clip set should be streamed in.
if(ShouldClipSetBeStreamedIn())
{
#if __BANK
if (CPrioritizedClipSetRequestManager::ms_bSpewDetailedRequests2)
{
streamPrioritizedDebugf1("Frame : %i, CPrioritizedClipSetRequest::Update dictionary %s being requested", fwTimer::GetFrameCount(), rClipSet.GetClipDictionaryName().GetCStr());
}
#endif // __BANK
//Request the clip set.
bIsClipSetStreamedIn = RequestClipSet(rClipSet);
}
#if __BANK
if (CPrioritizedClipSetRequestManager::ms_bSpewDetailedRequests2)
{
streamPrioritizedDebugf1("Frame : %i, CPrioritizedClipSetRequest::Update dictionary %s NOT being requested (m_nDesiredStreamingState = %i)", fwTimer::GetFrameCount(), rClipSet.GetClipDictionaryName().GetCStr(), (s32)m_nDesiredStreamingState);
}
#endif // __BANK
}
else
{
//Check if the clip set should be streamed out.
if(ShouldClipSetBeStreamedOut())
{
#if __BANK
if (CPrioritizedClipSetRequestManager::ms_bSpewDetailedRequests2)
{
streamPrioritizedDebugf1("Frame : %i, CPrioritizedClipSetRequest::Update dictionary %s being released (m_nDesiredStreamingState = %i)", fwTimer::GetFrameCount(), rClipSet.GetClipDictionaryName().GetCStr(), (s32)m_nDesiredStreamingState);
}
#endif // __BANK
//Check if we have set the don't delete flag.
if(HasSetDontDeleteFlag())
{
//Clear the don't delete flag.
ClearDontDeleteFlag(rClipSet);
}
//Check if we have added a reference to the clip set.
if(HasAddedReferenceToClipSet())
{
//Release the clip set.
ReleaseClipSet(rClipSet);
}
//Stream out the clip set.
StreamOutClipSet(rClipSet);
//Update the streamed in state, since it may have changed.
bIsClipSetStreamedIn = IsClipSetStreamedIn(rClipSet);
}
}
#if __BANK
if (CPrioritizedClipSetRequestManager::ms_bSpewDetailedRequests2)
{
streamPrioritizedDebugf1("Frame : %i, ClipsetStreamedIn ? %s, ShouldBeStreamedIn ? %s, HasAddedReference ? %s", fwTimer::GetFrameCount(), bIsClipSetStreamedIn ?"TRUE":"FALSE", ShouldClipSetBeStreamedIn() ?"TRUE":"FALSE", HasAddedReferenceToClipSet() ?"TRUE":"FALSE");
}
#endif // __BANK
//Check if the clip set has been streamed in, should be streamed in, and we have not added a reference.
if(bIsClipSetStreamedIn && ShouldClipSetBeStreamedIn() && !HasAddedReferenceToClipSet())
{
//Add a reference to the clip set.
AddReferenceToClipSet(rClipSet);
}
//Clear the override.
m_nStreamingPriorityOverride = SP_Invalid;
}
#if PRIORITIZED_CLIP_SET_STREAMER_WIDGETS
////////////////////////////////////////////////////////////////////////////////
// CPrioritizedClipSetBucketDebug
////////////////////////////////////////////////////////////////////////////////
#endif
////////////////////////////////////////////////////////////////////////////////
// CPrioritizedClipSetBucket
////////////////////////////////////////////////////////////////////////////////
FW_INSTANTIATE_CLASS_POOL(CPrioritizedClipSetBucket, CONFIGURED_FROM_FILE, atHashString("CPrioritizedClipSetBucket",0x6e126610));
CPrioritizedClipSetBucket::CPrioritizedClipSetBucket(atHashWithStringNotFinal hMemoryGroup)
: m_aRequests()
, m_hMemoryGroup(hMemoryGroup)
, m_MemoryBudget()
, m_MemoryUsage()
, m_uRunningFlags(0)
#if PRIORITIZED_CLIP_SET_STREAMER_WIDGETS
, m_Debug()
#endif
{
}
CPrioritizedClipSetBucket::~CPrioritizedClipSetBucket()
{
//Iterate over the requests.
int iCount = m_aRequests.GetCount();
for(int i = 0; i < iCount; ++i)
{
//Destroy the request.
DestroyRequest(i);
}
}
CPrioritizedClipSetRequest* CPrioritizedClipSetBucket::AddRequest(fwMvClipSetId clipSetId)
{
//Find the request for the clip set.
CPrioritizedClipSetRequest* pRequest = FindRequest(clipSetId);
if(!pRequest)
{
//Create a request.
pRequest = CreateRequest(clipSetId);
if(!streamPrioritizedVerifyf(pRequest, "Unable to create a request."))
{
return NULL;
}
}
//Add a reference.
pRequest->AddReference();
//Sort the requests.
m_uRunningFlags.SetFlag(RF_SortRequests);
return pRequest;
}
void CPrioritizedClipSetBucket::Initialize()
{
//Ensure the memory group metadata is valid.
fwMemoryGroupMetadata* pMetadata = fwClipSetManager::GetMemoryGroupMetadata(m_hMemoryGroup);
if(!streamPrioritizedVerifyf(pMetadata, "The metadata for memory group: %s is invalid.", m_hMemoryGroup.GetCStr()))
{
return;
}
//Get the memory budget.
u32 uMemoryBudget = pMetadata->GetMemoryBudget();
//The memory budget is defined in metadata as KB. Transform it to bytes.
uMemoryBudget = (uMemoryBudget << 10);
//Set the initial memory budget.
m_MemoryBudget.m_uInitial = uMemoryBudget;
}
void CPrioritizedClipSetBucket::RandomizeTiebreakers()
{
//Iterate over the requests.
int iCount = m_aRequests.GetCount();
for(int i = 0; i < iCount; ++i)
{
//Grab the request.
CPrioritizedClipSetRequest* pRequest = m_aRequests[i];
//Randomize the tiebreaker.
pRequest->RandomizeTiebreaker();
}
//Note that we should sort the requests.
m_uRunningFlags.SetFlag(RF_SortRequests);
}
void CPrioritizedClipSetBucket::RemoveRequest(fwMvClipSetId clipSetId)
{
//Ensure the index is valid.
int iIndex = FindRequestIndex(clipSetId);
if(!streamPrioritizedVerifyf(iIndex >= 0, "The index is invalid for clip set: %s.", clipSetId.GetCStr()))
{
return;
}
//Grab the request.
CPrioritizedClipSetRequest* pRequest = m_aRequests[iIndex];
//Remove a reference.
pRequest->RemoveReference();
//Check if the request is unreferenced.
if(pRequest->IsUnreferenced())
{
//Destroy the request.
DestroyRequest(pRequest);
//Remove the request.
m_aRequests.Delete(iIndex);
}
}
void CPrioritizedClipSetBucket::Update()
{
//Update the memory budget.
UpdateMemoryBudget();
//Update the requests.
UpdateRequests();
}
bool CPrioritizedClipSetBucket::Compare(const CPrioritizedClipSetRequest* pRequest, const CPrioritizedClipSetRequest* pOtherRequest)
{
//Check if the streaming priorities are different.
eStreamingPriority nStreamingPriority = pRequest->GetStreamingPriority();
eStreamingPriority nOtherStreamingPriority = pOtherRequest->GetStreamingPriority();
if(nStreamingPriority != nOtherStreamingPriority)
{
return (nStreamingPriority > nOtherStreamingPriority);
}
//Check if the reference values are different.
u8 uReferences = pRequest->GetReferences();
u8 uOtherReferences = pOtherRequest->GetReferences();
if(uReferences != uOtherReferences)
{
return (uReferences > uOtherReferences);
}
//Check if the tiebreaker values are different.
u8 uTiebreaker = pRequest->GetTiebreaker();
u8 uOtherTiebreaker = pOtherRequest->GetTiebreaker();
if(uTiebreaker != uOtherTiebreaker)
{
return (uTiebreaker > uOtherTiebreaker);
}
//Compare pointer values.
return (pRequest < pOtherRequest);
}
#if PRIORITIZED_CLIP_SET_STREAMER_WIDGETS
void CPrioritizedClipSetBucket::AddWidgets(bkGroup& rGroup)
{
//Add the group.
bkGroup* pGroup = rGroup.AddGroup(m_hMemoryGroup.GetCStr());
bkGroup* pRenderingGroup = pGroup->AddGroup("Rendering");
pRenderingGroup->AddToggle("Is Enabled", &m_Debug.m_Rendering.m_bIsEnabled);
bkGroup* pBudgetGroup = pGroup->AddGroup("Budget");
pBudgetGroup->AddToggle("Use Multiplier Override", &m_Debug.m_Budget.m_bUseMultiplierOverride);
pBudgetGroup->AddSlider("Multiplier Override:", &m_Debug.m_Budget.m_fMultiplierOverride, 0.0f, 100.0f, 0.1f);
}
CPrioritizedClipSetRequest* CPrioritizedClipSetBucket::GetRandomRequest() const
{
//Ensure the count is valid.
int iCount = m_aRequests.GetCount();
if(iCount <= 0)
{
return NULL;
}
//Generate a random index.
int iIndex = fwRandom::GetRandomNumberInRange(0, iCount);
return m_aRequests[iIndex];
}
void CPrioritizedClipSetBucket::RenderDebug(const Vector2& vPosition, int& iLine) const
{
#if DEBUG_DRAW
//Ensure rendering is enabled.
if(!m_Debug.m_Rendering.m_bIsEnabled)
{
return;
}
//Create a buffer.
char buf[512];
//Draw the title.
u32 uMemoryUsage = m_MemoryUsage.m_uCurrent >> 10;
u32 uMemoryBudget = m_MemoryBudget.m_uCurrent >> 10;
Color32 color = (uMemoryUsage > uMemoryBudget) ? Color_red : Color_green;
formatf(buf, "%s [Usage: %dK, Budget: %dK (%.2fx)]", m_hMemoryGroup.GetCStr(), uMemoryUsage, uMemoryBudget, m_MemoryBudget.m_fMultiplier);
grcDebugDraw::PrintToScreenCoors(buf, (s32)vPosition.x, (s32)vPosition.y + iLine++, color);
//Iterate over the requests.
int iCount = m_aRequests.GetCount();
for(int i = 0; i < iCount; ++i)
{
//Grab the request.
const CPrioritizedClipSetRequest* pRequest = m_aRequests[i];
//Ensure the clip dictionary index is valid.
int iClipDictionaryIndex = pRequest->GetClipDictionaryIndex();
if(!streamPrioritizedVerifyf(iClipDictionaryIndex >= 0, "The clip dictionary index is invalid for clip set id: %s.", pRequest->GetClipSetId().GetCStr()))
{
continue;
}
//Get the number of references.
strLocalIndex tempStrLocalIndex(iClipDictionaryIndex);
int iNumRefs = fwAnimManager::GetNumRefs(tempStrLocalIndex);
//Grab the streaming states.
bool bIsClipSetStreamedIn = pRequest->IsClipSetStreamedIn();
bool bShouldClipSetBeStreamedIn = pRequest->ShouldClipSetBeStreamedIn();
//Calculate the color.
if(!bIsClipSetStreamedIn && bShouldClipSetBeStreamedIn)
{
color = Color_orange;
}
else if(bIsClipSetStreamedIn && !bShouldClipSetBeStreamedIn)
{
color = Color_yellow;
}
else if(bIsClipSetStreamedIn)
{
color = Color_green;
}
else
{
color = Color_red;
}
//Check if the request has added a reference to the clip set.
bool bHasAddedReferenceToClipSet = pRequest->HasAddedReferenceToClipSet();
//Calculate the memory cost.
u32 uMemoryCost = CPrioritizedClipSetStreamer::GetInstance().CalculateMemoryCostForClipDictionary(tempStrLocalIndex);
//Draw the request.
formatf(buf, "%s [Cost: %dK] [Pri: %d, Refs: %d, Str: %s, Des: %s, Ref'd: %s] [Refs: %d]",
pRequest->GetClipSetId().GetCStr(),
uMemoryCost >> 10,
pRequest->GetStreamingPriority(),
pRequest->GetReferences(),
bIsClipSetStreamedIn ? "Yes" : "No",
bShouldClipSetBeStreamedIn ? "Yes" : "No",
bHasAddedReferenceToClipSet ? "Yes" : "No",
iNumRefs);
grcDebugDraw::PrintToScreenCoors(buf, (s32)vPosition.x, (s32)vPosition.y + iLine++, color);
}
#endif
}
#endif
CPrioritizedClipSetRequest* CPrioritizedClipSetBucket::CreateRequest(fwMvClipSetId clipSetId)
{
//Ensure we have not exceeded the maximum requests.
if(!streamPrioritizedVerifyf(!m_aRequests.IsFull(), "The requests are full."))
{
return NULL;
}
//Ensure there is room in the pool.
if(!streamPrioritizedVerifyf(CPrioritizedClipSetRequest::GetPool()->GetNoOfFreeSpaces() > 0, "The request pool is full."))
{
return NULL;
}
//Create the request.
CPrioritizedClipSetRequest* pRequest = rage_new CPrioritizedClipSetRequest(clipSetId);
//Initialize the request.
pRequest->Initialize();
//Add the request.
m_aRequests.Append() = pRequest;
return pRequest;
}
void CPrioritizedClipSetBucket::DestroyRequest(int iIndex)
{
//Destroy the request.
DestroyRequest(m_aRequests[iIndex]);
}
void CPrioritizedClipSetBucket::DestroyRequest(CPrioritizedClipSetRequest* pRequest)
{
//Ensure the request is valid.
if(!pRequest)
{
return;
}
//Free the request.
delete pRequest;
}
CPrioritizedClipSetRequest* CPrioritizedClipSetBucket::FindRequest(fwMvClipSetId clipSetId)
{
return const_cast<CPrioritizedClipSetRequest *>(static_cast<const CPrioritizedClipSetBucket &>(*this).FindRequest(clipSetId));
}
const CPrioritizedClipSetRequest* CPrioritizedClipSetBucket::FindRequest(fwMvClipSetId clipSetId) const
{
//Find the request index.
int iIndex = FindRequestIndex(clipSetId);
if(iIndex < 0)
{
return NULL;
}
return m_aRequests[iIndex];
}
int CPrioritizedClipSetBucket::FindRequestIndex(fwMvClipSetId clipSetId) const
{
//Iterate over the requests.
int iCount = m_aRequests.GetCount();
for(int i = 0; i < iCount; ++i)
{
//Ensure the clip set matches.
const CPrioritizedClipSetRequest* pRequest = m_aRequests[i];
if(clipSetId != pRequest->GetClipSetId())
{
continue;
}
return i;
}
return -1;
}
float CPrioritizedClipSetBucket::GenerateMultiplierForMemoryBudget() const
{
#if PRIORITIZED_CLIP_SET_STREAMER_WIDGETS
//Check if we should use the multiplier override.
if(m_Debug.m_Budget.m_bUseMultiplierOverride)
{
return m_Debug.m_Budget.m_fMultiplierOverride;
}
#endif
return 1.0f;
}
void CPrioritizedClipSetBucket::SortRequests()
{
//Ensure the request count is valid.
int iCount = m_aRequests.GetCount();
if(iCount <= 0)
{
return;
}
//Sort the requests.
CPrioritizedClipSetRequest** pRequests = &m_aRequests[0];
std::sort(pRequests, pRequests + iCount, CPrioritizedClipSetBucket::Compare);
}
void CPrioritizedClipSetBucket::UpdateMemoryBudget()
{
//Calculate the adjusted budget.
//This is done by applying the adjustment to the initial value.
u32 uAdjusted = (u32)Max(0, ((s32)m_MemoryBudget.m_uInitial) + m_MemoryBudget.m_iAdjustment);
//Generate the memory budget multiplier.
float fMultiplier = GenerateMultiplierForMemoryBudget();
m_MemoryBudget.m_fMultiplier = fMultiplier;
//Calculate the current budget.
u32 uCurrent = (u32)((float)uAdjusted * fMultiplier);
m_MemoryBudget.m_uCurrent = uCurrent;
}
void CPrioritizedClipSetBucket::UpdateRequests()
{
//Check if we should sort the requests.
if(m_uRunningFlags.IsFlagSet(RF_SortRequests))
{
//Clear the flag.
m_uRunningFlags.ClearFlag(RF_SortRequests);
//Sort the requests.
SortRequests();
}
//Calculate the current memory usage and budget.
u32 uUsage = m_MemoryUsage.m_uCurrent;
u32 uBudget = m_MemoryBudget.m_uCurrent;
//Count the requests.
int iCount = m_aRequests.GetCount();
//The top of the request list gets highest priority.
int iRequestToStreamIn = 0;
//The bottom of the request list gets lowest priority.
int iRequestToStreamOut = iCount - 1;
#if __BANK
bool bShouldSpewDebug = false;
if (CPrioritizedClipSetRequestManager::ms_bSpewDetailedRequests)
{
static atHashString MG_VEHICLE("MG_Vehicle");
if (m_hMemoryGroup == MG_VEHICLE)
{
bShouldSpewDebug = true;
CPrioritizedClipSetRequestManager::ms_bSpewDetailedRequests2 = true;
streamPrioritizedDebugf1("Frame : %i, CPrioritizedClipSetBucket::UpdateRequests called for bucket processing memory group %s, usage %u, budget %u, count %i", fwTimer::GetFrameCount(), MG_VEHICLE.GetCStr(), uUsage, uBudget, iCount);
}
}
#endif // __BANK
//Iterate over the requests.
while(iRequestToStreamIn <= iRequestToStreamOut)
{
//Grab the request to stream in.
CPrioritizedClipSetRequest* pRequestToStreamIn = m_aRequests[iRequestToStreamIn];
//Note: At some point we will probably want to take into account hits to other memory groups that will occur
// when streaming in a clip set, due to the fallback clip sets. This is really complicated, though...
//Check if the clip set is valid.
fwClipSet* pClipSetToStreamIn = pRequestToStreamIn->GetClipSet();
if(streamPrioritizedVerifyf(pClipSetToStreamIn, "The clip set with id: %s is invalid.", pRequestToStreamIn->GetClipSetId().GetCStr()))
{
//Calculate the cost to stream in the clip set.
u32 uCostToStreamIn = CPrioritizedClipSetStreamer::GetInstance().CalculateMemoryCostForClipSetToStreamIn(*pClipSetToStreamIn, m_hMemoryGroup);
//Check if the clip set is high priority.
bool bIsHighPriority = (pRequestToStreamIn->GetStreamingPriority() == SP_High);
//Check if the clip sets is within the budget.
bool bIsWithinBudget = (uUsage + uCostToStreamIn <= uBudget);
#if __BANK
if (bShouldSpewDebug)
{
streamPrioritizedDebugf1("Frame : %i, Evaluating clipset %s, high priority ? %s, within budget ? %s", fwTimer::GetFrameCount(), pRequestToStreamIn->GetClipSetId().GetCStr(), bIsHighPriority ? "TRUE":"FALSE", bIsWithinBudget ? "TRUE":"FALSE");
}
#endif // __BANK
//Check if the clip set should be streamed in.
if(bIsHighPriority || bIsWithinBudget)
{
//Stream in the clip set.
pRequestToStreamIn->SetDesiredStreamingState(CPrioritizedClipSetRequest::DSS_ShouldBeStreamedIn);
//Update the request.
pRequestToStreamIn->Update(*pClipSetToStreamIn);
//Increase the usage.
uUsage += uCostToStreamIn;
//Move to the next request.
++iRequestToStreamIn;
}
else
{
//Grab the request to stream out.
CPrioritizedClipSetRequest* pRequestToStreamOut = m_aRequests[iRequestToStreamOut];
//Check if the clip set is valid.
fwClipSet* pClipSetToStreamOut = pRequestToStreamOut->GetClipSet();
if(streamPrioritizedVerifyf(pClipSetToStreamOut, "The clip set with id: %s is invalid.", pRequestToStreamOut->GetClipSetId().GetCStr()))
{
//Check if the clip dictionary is counted against the memory cost.
strLocalIndex iClipDictionaryIndexToStreamOut = pClipSetToStreamOut->GetClipDictionaryIndex();
bool bIsClipDictionaryToStreamOutCountedAgainstMemoryCost = (iClipDictionaryIndexToStreamOut.Get() >= 0) ? CPrioritizedClipSetStreamer::GetInstance().IsClipDictionaryCountedAgainstMemoryCost(iClipDictionaryIndexToStreamOut.Get()) : false;
//Stream out the clip set.
pRequestToStreamOut->SetDesiredStreamingState(CPrioritizedClipSetRequest::DSS_ShouldBeStreamedOut);
//Update the request.
pRequestToStreamOut->Update(*pClipSetToStreamOut);
//Check if the clip dictionary was counted against the memory cost.
if(bIsClipDictionaryToStreamOutCountedAgainstMemoryCost)
{
//Check if the clip dictionary should not be counted against the memory cost.
bool bShouldClipDictionaryToStreamOutCountAgainstMemoryCost = CPrioritizedClipSetStreamer::GetInstance().ShouldClipDictionaryCountAgainstMemoryCost(iClipDictionaryIndexToStreamOut);
if(!bShouldClipDictionaryToStreamOutCountAgainstMemoryCost)
{
//Calculate the refund.
u32 uRefund = CPrioritizedClipSetStreamer::GetInstance().CalculateMemoryCostForClipDictionary(iClipDictionaryIndexToStreamOut);
//Decrease the usage.
streamPrioritizedAssertf(uUsage >= uRefund, "The refund: %d exceeds the usage: %d.", uRefund, uUsage);
uUsage -= uRefund;
}
}
//Move to the previous request.
--iRequestToStreamOut;
}
else
{
//Move to the previous request.
--iRequestToStreamOut;
}
}
}
else
{
//Move to the next request.
++iRequestToStreamIn;
}
}
#if __BANK
if (bShouldSpewDebug)
{
CPrioritizedClipSetRequestManager::ms_bSpewDetailedRequests2 = false;
}
#endif // __BANK
}
#if PRIORITIZED_CLIP_SET_STREAMER_WIDGETS
////////////////////////////////////////////////////////////////////////////////
// CPrioritizedClipSetStreamerDebug
////////////////////////////////////////////////////////////////////////////////
void CPrioritizedClipSetStreamerDebug::Metadata::DumpMissingToOutput()
{
//Display the title.
Displayf("Clip dictionaries with missing metadata:");
//Iterate over the clip dictionaries.
int iCount = fwAnimManager::GetSize();
for(int i = 0; i < iCount; ++i)
{
//Ensure the slot is valid.
strLocalIndex tempStrLocalIndex(i);
if(!fwAnimManager::IsValidSlot(tempStrLocalIndex))
{
continue;
}
//Ensure the clip dictionary metadata is valid.
atHashString hName = fwAnimManager::FindHashKeyFromSlot(tempStrLocalIndex);
fwClipDictionaryMetadata* pMetadata = fwClipSetManager::GetClipDictionaryMetadata(hName);
if(pMetadata)
{
continue;
}
//Display the name.
Displayf(" %s", hName.GetCStr());
}
}
void CPrioritizedClipSetStreamerDebug::RequestController::Individual::AddRequest()
{
//Add the request.
CPrioritizedClipSetStreamer::GetInstance().AddRequest(m_ClipSelector.GetSelectedClipSetId());
}
void CPrioritizedClipSetStreamerDebug::RequestController::Individual::RemoveRequest()
{
//Remove the request.
CPrioritizedClipSetStreamer::GetInstance().RemoveRequest(m_ClipSelector.GetSelectedClipSetId());
}
void CPrioritizedClipSetStreamerDebug::RequestController::Bulk::AddRandomRequests()
{
//Count the clip sets.
int iClipSetCount = fwClipSetManager::GetClipSetCount();
//Iterate over the amount.
for(int i = 0; i < m_iAmount; ++i)
{
//Generate a random index.
int iIndex = fwRandom::GetRandomNumberInRange(0, iClipSetCount);
//Grab the clip set.
fwMvClipSetId clipSetId = fwClipSetManager::GetClipSetIdByIndex(iIndex);
//Add the request.
CPrioritizedClipSetStreamer::GetInstance().AddRequest(clipSetId);
}
}
void CPrioritizedClipSetStreamerDebug::RequestController::Bulk::RemoveRandomRequests()
{
//Iterate over the amount.
for(int i = 0; i < m_iAmount; ++i)
{
//Generate a random bucket.
CPrioritizedClipSetBucket* pBucket = CPrioritizedClipSetStreamer::GetInstance().GetRandomBucket();
if(!pBucket)
{
continue;
}
//Generate a random request.
CPrioritizedClipSetRequest* pRequest = pBucket->GetRandomRequest();
if(!pRequest)
{
continue;
}
//Remove all references.
u8 uReferences = pRequest->GetReferences();
for(u8 i = 0; i < uReferences; ++i)
{
//Remove the request.
CPrioritizedClipSetStreamer::GetInstance().RemoveRequest(pRequest->GetClipSetId());
}
}
}
#endif
////////////////////////////////////////////////////////////////////////////////
// CPrioritizedClipSetStreamer
////////////////////////////////////////////////////////////////////////////////
CPrioritizedClipSetStreamer* CPrioritizedClipSetStreamer::sm_Instance = NULL;
CPrioritizedClipSetStreamer::CPrioritizedClipSetStreamer()
: m_IsClipDictionaryCountedAgainstMemoryCost()
, m_aBuckets()
, m_fTimeSinceLastTiebreakersRandomization(0.0f)
, m_iClipDictionaryIndexToCheck(0)
, m_hSituation()
#if PRIORITIZED_CLIP_SET_STREAMER_WIDGETS
, m_Debug()
#endif
{
}
CPrioritizedClipSetStreamer::~CPrioritizedClipSetStreamer()
{
#if PRIORITIZED_CLIP_SET_STREAMER_WIDGETS
//Remove the widgets.
RemoveWidgets();
#endif
//Iterate over the buckets.
int iCount = m_aBuckets.GetCount();
for(int i = iCount-1; i >= 0; --i)
{
delete m_aBuckets[i];
m_aBuckets[i] = NULL;
}
}
CPrioritizedClipSetRequest* CPrioritizedClipSetStreamer::AddRequest(fwMvClipSetId clipSetId)
{
//Ensure the clip set is valid.
if(!streamPrioritizedVerifyf(clipSetId != CLIP_SET_ID_INVALID, "The clip set is invalid."))
{
return NULL;
}
//Find the bucket for the clip set.
CPrioritizedClipSetBucket* pBucket = FindBucket(clipSetId);
if(!streamPrioritizedVerifyf(pBucket, "Unable to find bucket for clip set: %s.", clipSetId.GetCStr()))
{
return NULL;
}
//Add the request.
return pBucket->AddRequest(clipSetId);
}
u32 CPrioritizedClipSetStreamer::CalculateMemoryCostForClipDictionary(strLocalIndex iIndex) const
{
//Assert that the slot is valid.
streamPrioritizedAssertf(fwAnimManager::IsValidSlot(strLocalIndex(iIndex)), "The slot is invalid: %d.", iIndex.Get());
//Grab the streaming module id.
s32 iModuleId = fwAnimManager::GetStreamingModuleId();
//Ensure the object is in the image.
if(!CStreaming::IsObjectInImage(iIndex, iModuleId))
{
return 0;
}
//Grab the streaming index.
strIndex index = strStreamingEngine::GetInfo().GetModuleMgr().GetModule(iModuleId)->GetStreamingIndex(strLocalIndex(iIndex));
//Get the cost.
u32 uCost = strStreamingEngine::GetInfo().GetObjectVirtualSize(index, true);
return uCost;
}
u32 CPrioritizedClipSetStreamer::CalculateMemoryCostForClipSetToStreamIn(const fwClipSet& rClipSet, atHashWithStringNotFinal hMemoryGroup) const
{
//Keep track of the cost.
u32 uCostToStreamIn = 0;
//Iterate over the clip sets.
const fwClipSet* pClipSet = &rClipSet;
while(pClipSet)
{
//Check if the clip dictionary index is valid.
strLocalIndex iClipDictionaryIndex = pClipSet->GetClipDictionaryIndex();
if(streamPrioritizedVerifyf(iClipDictionaryIndex.Get() >= 0, "The clip dictionary index for clip set with clip dictionary name: %s is invalid.", pClipSet->GetClipDictionaryName().GetCStr()))
{
//Check if the clip dictionary is not counted against the memory cost.
if(!IsClipDictionaryCountedAgainstMemoryCost(iClipDictionaryIndex.Get()))
{
//Check if the memory group matches.
if(GetMemoryGroupForClipDictionary(iClipDictionaryIndex) == hMemoryGroup)
{
//Calculate the cost to stream in.
uCostToStreamIn += CalculateMemoryCostForClipDictionary(iClipDictionaryIndex);
}
}
}
//Ensure the fallback clip set id is valid.
fwMvClipSetId fallbackClipSetId = pClipSet->GetFallbackId();
if(fallbackClipSetId == CLIP_SET_ID_INVALID)
{
break;
}
//Ensure the fallback clip set is valid.
pClipSet = pClipSet->GetFallbackSet();
if(!streamPrioritizedVerifyf(pClipSet, "The clip set with id: %s is invalid.", fallbackClipSetId.GetCStr()))
{
break;
}
}
return uCostToStreamIn;
}
const CPrioritizedClipSetRequest* CPrioritizedClipSetStreamer::FindRequest(fwMvClipSetId clipSetId) const
{
//Ensure the bucket is valid.
const CPrioritizedClipSetBucket* pBucket = FindBucket(clipSetId);
if(!pBucket)
{
return NULL;
}
return (pBucket->FindRequest(clipSetId));
}
atHashWithStringNotFinal CPrioritizedClipSetStreamer::GetMemoryGroupForClipDictionary(strLocalIndex iIndex) const
{
//Assert that the slot is valid.
streamPrioritizedAssertf(fwAnimManager::IsValidSlot(iIndex), "The slot is invalid: %d.", iIndex.Get());
//Ensure the metadata is valid.
atHashString hName = fwAnimManager::FindHashKeyFromSlot(iIndex);
fwClipDictionaryMetadata* pMetadata = fwClipSetManager::GetClipDictionaryMetadata(hName);
return pMetadata ? pMetadata->GetMemoryGroup() : s_hDefaultMemoryGroup;
}
void CPrioritizedClipSetStreamer::Initialize()
{
//Initialize the buckets.
InitializeBuckets();
//Initialize the memory costs.
InitializeMemoryCosts();
}
void CPrioritizedClipSetStreamer::OnClipDictionaryAllRefsReset(strLocalIndex iIndex)
{
//Update the memory cost for the clip dictionary.
UpdateMemoryCostForClipDictionary(iIndex);
}
void CPrioritizedClipSetStreamer::OnClipDictionaryLoaded(strLocalIndex iIndex)
{
//Update the memory cost for the clip dictionary.
UpdateMemoryCostForClipDictionary(iIndex);
}
void CPrioritizedClipSetStreamer::OnClipDictionaryRemoved(strLocalIndex iIndex)
{
//Update the memory cost for the clip dictionary.
UpdateMemoryCostForClipDictionary(iIndex);
}
void CPrioritizedClipSetStreamer::OnClipDictionaryRefAdded(strLocalIndex iIndex)
{
//Update the memory cost for the clip dictionary.
UpdateMemoryCostForClipDictionary(iIndex);
}
void CPrioritizedClipSetStreamer::OnClipDictionaryRefRemoved(strLocalIndex iIndex)
{
//Update the memory cost for the clip dictionary.
UpdateMemoryCostForClipDictionary(iIndex);
}
void CPrioritizedClipSetStreamer::OnClipDictionaryRefRemovedWithoutDelete(strLocalIndex iIndex)
{
//Update the memory cost for the clip dictionary.
UpdateMemoryCostForClipDictionary(iIndex);
}
void CPrioritizedClipSetStreamer::RemoveRequest(fwMvClipSetId clipSetId)
{
//Ensure the clip set is valid.
if(!streamPrioritizedVerifyf(clipSetId != CLIP_SET_ID_INVALID, "The clip set is invalid."))
{
return;
}
//Ensure the index is valid.
int iIndex = FindBucketIndex(clipSetId);
if(!streamPrioritizedVerifyf(iIndex >= 0, "The index is invalid for clip set: %s.", clipSetId.GetCStr()))
{
return;
}
//Grab the bucket.
CPrioritizedClipSetBucket* pBucket = m_aBuckets[iIndex];
//Remove the request.
pBucket->RemoveRequest(clipSetId);
}
bool CPrioritizedClipSetStreamer::ShouldClipDictionaryCountAgainstMemoryCost(strLocalIndex iIndex) const
{
//Assert that the index is valid.
animAssertf(fwAnimManager::IsValidSlot(strLocalIndex(iIndex)), "The index is invalid: %d.", iIndex.Get());
//Ensure the number of references is valid.
int iNumRefs = fwAnimManager::GetNumRefs(strLocalIndex(iIndex));
if(iNumRefs <= 0)
{
return false;
}
//Grab the streaming module id.
s32 iModuleId = fwAnimManager::GetStreamingModuleId();
//Ensure the object has loaded.
if(!CStreaming::HasObjectLoaded(iIndex, iModuleId))
{
return false;
}
return true;
}
void CPrioritizedClipSetStreamer::Update(float fTimeStep)
{
//Update the timers.
UpdateTimers(fTimeStep);
//Update the situation.
UpdateSituation();
//Update the memory costs.
UpdateMemoryCosts();
//Update the buckets.
UpdateBuckets();
}
void CPrioritizedClipSetStreamer::Init(unsigned UNUSED_PARAM(initMode))
{
//Init the pools.
CPrioritizedClipSetBucket::InitPool(MEMBUCKET_GAMEPLAY);
CPrioritizedClipSetRequest::InitPool(MEMBUCKET_GAMEPLAY);
//Create the instance.
Assert(!sm_Instance);
sm_Instance = rage_new CPrioritizedClipSetStreamer;
//Initialize the streamer.
sm_Instance->Initialize();
}
void CPrioritizedClipSetStreamer::Shutdown(unsigned UNUSED_PARAM(shutdownMode))
{
//Free the instance.
Assert(sm_Instance);
delete sm_Instance;
sm_Instance = NULL;
//Shutdown the pools.
CPrioritizedClipSetBucket::ShutdownPool();
CPrioritizedClipSetRequest::ShutdownPool();
}
void CPrioritizedClipSetStreamer::Update()
{
if (!fwTimer::IsGamePaused())
{
//Grab the time step.
float fTimeStep = fwTimer::GetTimeStep();
//Update the instance.
GetInstance().Update(fTimeStep);
}
#if !__FINAL
else
{
streamPrioritizedDebugf2("Frame : %i, Unable to process CPrioritizedClipSetStreamer because we think the game is paused", fwTimer::GetFrameCount());
streamPrioritizedDebugf2("fwTimer::IsGamePaused() ? %s, fwTimer::IsDebugPause() ? %s", fwTimer::IsGamePaused() ? "TRUE" : "FALSE", fwTimer::IsDebugPause() ? "TRUE" : "FALSE");
}
#endif // !__FINAL
}
#if PRIORITIZED_CLIP_SET_STREAMER_WIDGETS
void CPrioritizedClipSetStreamer::AddWidgets(bkBank& rBank)
{
//Ensure the group is invalid.
if(!streamPrioritizedVerifyf(!m_Debug.m_pGroup, "The group is valid."))
{
return;
}
m_Debug.m_pGroup = rBank.PushGroup("Prioritized Clip Set Streamer");
rBank.PushGroup("Rendering (requires debug text display [b])");
rBank.PushGroup("Requests");
rBank.AddToggle("Is Enabled", &m_Debug.m_Rendering.m_Requests.m_bIsEnabled);
rBank.PopGroup();
rBank.PushGroup("Clip Dictionaries");
rBank.AddToggle("Counted Against Cost", &m_Debug.m_Rendering.m_ClipDictionaries.m_bCountedAgainstCost);
rBank.AddToggle("Not Counted Against Cost", &m_Debug.m_Rendering.m_ClipDictionaries.m_bNotCountedAgainstCost);
rBank.AddText("Memory Group Name Filter", m_Debug.m_Rendering.m_ClipDictionaries.m_aMemoryGroupNameFilter, sizeof(m_Debug.m_Rendering.m_ClipDictionaries.m_aMemoryGroupNameFilter), false);
rBank.PopGroup();
rBank.AddSlider("Horizontal Scroll:", &m_Debug.m_Rendering.m_vScroll.x, 0.0f, 100.0f, 1.0f);
rBank.AddSlider("Vertical Scroll:", &m_Debug.m_Rendering.m_vScroll.y, -500.0f, 0.0f, 1.0f);
rBank.PopGroup();
bkGroup* pGroupForBuckets = rBank.PushGroup("Buckets");
int iCount = m_aBuckets.GetCount();
for(int i = 0; i < iCount; ++i)
{
m_aBuckets[i]->AddWidgets(*pGroupForBuckets);
}
rBank.PopGroup();
rBank.PushGroup("Metadata");
rBank.AddButton("Dump Missing To Output", datCallback(MFA(CPrioritizedClipSetStreamerDebug::Metadata::DumpMissingToOutput), &m_Debug.m_Metadata));
rBank.PopGroup();
rBank.PushGroup("Request Controller");
rBank.PushGroup("Individual");
m_Debug.m_RequestController.m_Individual.m_ClipSelector.SetShowClipSetClipNames(false);
m_Debug.m_RequestController.m_Individual.m_ClipSelector.AddWidgets(&rBank);
rBank.AddButton("Add Request", datCallback(MFA(CPrioritizedClipSetStreamerDebug::RequestController::Individual::AddRequest), &m_Debug.m_RequestController.m_Individual));
rBank.AddButton("Remove Request", datCallback(MFA(CPrioritizedClipSetStreamerDebug::RequestController::Individual::RemoveRequest), &m_Debug.m_RequestController.m_Individual));
rBank.PopGroup();
rBank.PushGroup("Bulk");
rBank.AddSlider("Amount:", &m_Debug.m_RequestController.m_Bulk.m_iAmount, 1, 128, 1);
rBank.AddButton("Add Random Requests", datCallback(MFA(CPrioritizedClipSetStreamerDebug::RequestController::Bulk::AddRandomRequests), &m_Debug.m_RequestController.m_Bulk));
rBank.AddButton("Remove Random Requests", datCallback(MFA(CPrioritizedClipSetStreamerDebug::RequestController::Bulk::RemoveRandomRequests), &m_Debug.m_RequestController.m_Bulk));
rBank.PopGroup();
rBank.PopGroup();
rBank.PopGroup();
}
CPrioritizedClipSetBucket* CPrioritizedClipSetStreamer::GetRandomBucket() const
{
//Ensure the count is valid.
int iCount = m_aBuckets.GetCount();
if(iCount <= 0)
{
return NULL;
}
//Generate a random index.
int iIndex = fwRandom::GetRandomNumberInRange(0, iCount);
return m_aBuckets[iIndex];
}
void CPrioritizedClipSetStreamer::RemoveWidgets()
{
//Ensure the group is valid.
bkGroup* pGroup = m_Debug.m_pGroup;
if(!pGroup)
{
return;
}
//Grab the bank.
bkBank* pBank = static_cast<bkBank *>(pGroup->GetParent());
//Remove the widgets for the clip selector.
m_Debug.m_RequestController.m_Individual.m_ClipSelector.RemoveWidgets(pBank);
m_Debug.m_RequestController.m_Individual.m_ClipSelector.Shutdown();
//Destroy the group.
pGroup->Destroy();
}
void CPrioritizedClipSetStreamer::RenderDebug() const
{
#if DEBUG_DRAW
//Initialize the position.
static Vector2 s_vInitialPosition(10.0f, 10.0f);
Vector2 vPosition(s_vInitialPosition);
vPosition += m_Debug.m_Rendering.m_vScroll;
//Keep track of the line.
int iLine = 0;
//Create a buffer.
char buf[512];
//Check if we should render requests.
if(m_Debug.m_Rendering.m_Requests.m_bIsEnabled)
{
//Draw the title.
formatf(buf, "Prioritized Clip Set Requests");
grcDebugDraw::PrintToScreenCoors(buf, (s32)vPosition.x, (s32)vPosition.y + iLine++, Color_white);
//Iterate over the buckets.
int iCount = m_aBuckets.GetCount();
for(int i = 0; i < iCount; ++i)
{
//Grab the bucket.
const CPrioritizedClipSetBucket* pBucket = m_aBuckets[i];
//Render the debug text for the bucket.
pBucket->RenderDebug(vPosition, iLine);
//Check if the bucket has requests, or this is the last bucket.
bool bIsLastBucket = (i + 1 == iCount);
if(pBucket->HasRequests() || bIsLastBucket)
{
//Add an empty line.
++iLine;
}
}
}
//Check if we should render the streamed in clip dictionaries.
bool bShouldRenderClipDictionariesCountedAgainstCost = m_Debug.m_Rendering.m_ClipDictionaries.m_bCountedAgainstCost;
bool bShouldRenderClipDictionariesNotCountedAgainstCost = m_Debug.m_Rendering.m_ClipDictionaries.m_bNotCountedAgainstCost;
if(bShouldRenderClipDictionariesCountedAgainstCost || bShouldRenderClipDictionariesNotCountedAgainstCost)
{
//Draw the title.
formatf(buf, "Clip Dictionaries");
grcDebugDraw::PrintToScreenCoors(buf, (s32)vPosition.x, (s32)vPosition.y + iLine++, Color_white);
//Hash the memory group filter.
atHashString hMemoryGroupFilter = m_Debug.m_Rendering.m_ClipDictionaries.m_aMemoryGroupNameFilter;
//Iterate over the clip dictionaries.
int iCount = fwAnimManager::GetSize();
for(int i = 0; i < iCount; ++i)
{
//Ensure the slot is valid.
strLocalIndex tempStrLocalIndex(i);
if(!fwAnimManager::IsValidSlot(tempStrLocalIndex))
{
continue;
}
//Ensure the memory group passes the filter.
atHashWithStringNotFinal hMemoryGroup = GetMemoryGroupForClipDictionary(tempStrLocalIndex);
if(hMemoryGroupFilter.IsNotNull() && (hMemoryGroup != hMemoryGroupFilter))
{
continue;
}
//Check if the clip dictionary is counted against the cost.
bool bCountedAgainstCost = IsClipDictionaryCountedAgainstMemoryCost(i);
if((bCountedAgainstCost && bShouldRenderClipDictionariesCountedAgainstCost) ||
(!bCountedAgainstCost && bShouldRenderClipDictionariesNotCountedAgainstCost))
{
//Render the clip dictionary.
}
else
{
continue;
}
//Grab the name.
atHashString hName = fwAnimManager::FindHashKeyFromSlot(tempStrLocalIndex);
//Calculate the cost.
u32 uCost = CalculateMemoryCostForClipDictionary(tempStrLocalIndex);
uCost = uCost >> 10;
//Calculate the number of references.
int iNumRefs = fwAnimManager::GetNumRefs(tempStrLocalIndex);
//Draw the streamed in clip dictionary.
formatf(buf, "%s (%s) [Cost: %dK] [Refs: %d]", hName.GetCStr(), hMemoryGroup.GetCStr(), uCost, iNumRefs);
grcDebugDraw::PrintToScreenCoors(buf, (s32)vPosition.x, (s32)vPosition.y + iLine++, bCountedAgainstCost ? Color_green : Color_red);
}
}
#endif
}
#endif
void CPrioritizedClipSetStreamer::ApplyMemoryBudgetAdjustments()
{
//Ensure the situation is valid.
if(m_hSituation.IsNull())
{
return;
}
//Ensure the memory situation is valid.
const fwMemorySituation* pMemorySituation = fwClipSetManager::GetMemorySituation(m_hSituation);
if(!streamPrioritizedVerifyf(pMemorySituation, "The memory situation: %s is invalid.", m_hSituation.GetCStr()))
{
return;
}
//First, we need to clear all of the adjustments.
//Iterate over the buckets.
int iNumBuckets = m_aBuckets.GetCount();
for(int i = 0; i < iNumBuckets; ++i)
{
//Grab the bucket.
CPrioritizedClipSetBucket* pBucket = m_aBuckets[i];
//Clear the memory budget adjustment.
pBucket->SetMemoryBudgetAdjustment(0);
}
//Apply the adjustments.
int iNumAdjustments = pMemorySituation->m_Adjustments.GetCount();
for(int i = 0; i < iNumAdjustments; ++i)
{
//Grab the adjustment.
const fwMemorySituation::Adjustment& rAdjustment = pMemorySituation->m_Adjustments[i];
//Ensure the bucket is valid.
CPrioritizedClipSetBucket* pBucket = FindBucket(rAdjustment.m_MemoryGroup);
if(!streamPrioritizedVerifyf(pBucket, "The memory group: %s is invalid.", rAdjustment.m_MemoryGroup.GetCStr()))
{
continue;
}
//The amount is defined in metadata as KB. Transform it to bytes.
int iAmount = (rAdjustment.m_Amount << 10);
//Set the memory budget adjustments.
pBucket->SetMemoryBudgetAdjustment(iAmount);
}
}
atHashWithStringNotFinal CPrioritizedClipSetStreamer::CalculateSituation() const
{
//Check if we are in combat.
if(CGameSituation::GetInstance().GetFlags().IsFlagSet(CGameSituation::Combat))
{
static atHashWithStringNotFinal s_hCombat("Combat",0x57779727);
return s_hCombat;
}
else
{
static atHashWithStringNotFinal s_hDefault("Default",0xE4DF46D5);
return s_hDefault;
}
}
bool CPrioritizedClipSetStreamer::CanUpdateSituation() const
{
//Ensure the game is not paused.
if(fwTimer::IsGamePaused())
{
return false;
}
return true;
}
CPrioritizedClipSetBucket* CPrioritizedClipSetStreamer::CreateBucket(atHashWithStringNotFinal hMemoryGroup)
{
//Ensure we have not exceeded the maximum buckets.
if(!streamPrioritizedVerifyf(!m_aBuckets.IsFull(), "The buckets are full."))
{
return NULL;
}
//Ensure there is room in the pool.
if(!streamPrioritizedVerifyf(CPrioritizedClipSetBucket::GetPool()->GetNoOfFreeSpaces() > 0, "The bucket pool is full."))
{
return NULL;
}
//Create the bucket.
CPrioritizedClipSetBucket* pBucket = rage_new CPrioritizedClipSetBucket(hMemoryGroup);
//Initialize the bucket.
pBucket->Initialize();
//Add the bucket.
m_aBuckets.Append() = pBucket;
return pBucket;
}
CPrioritizedClipSetBucket* CPrioritizedClipSetStreamer::FindBucket(fwMvClipSetId clipSetId)
{
return const_cast<CPrioritizedClipSetBucket *>(static_cast<const CPrioritizedClipSetStreamer &>(*this).FindBucket(clipSetId));
}
const CPrioritizedClipSetBucket* CPrioritizedClipSetStreamer::FindBucket(fwMvClipSetId clipSetId) const
{
return (FindBucket(GetMemoryGroup(clipSetId)));
}
CPrioritizedClipSetBucket* CPrioritizedClipSetStreamer::FindBucket(atHashWithStringNotFinal hMemoryGroup)
{
return const_cast<CPrioritizedClipSetBucket *>(static_cast<const CPrioritizedClipSetStreamer &>(*this).FindBucket(hMemoryGroup));
}
const CPrioritizedClipSetBucket* CPrioritizedClipSetStreamer::FindBucket(atHashWithStringNotFinal hMemoryGroup) const
{
//Find the bucket index.
int iIndex = FindBucketIndex(hMemoryGroup);
if(iIndex < 0)
{
return NULL;
}
return m_aBuckets[iIndex];
}
int CPrioritizedClipSetStreamer::FindBucketIndex(fwMvClipSetId clipSetId) const
{
return (FindBucketIndex(GetMemoryGroup(clipSetId)));
}
int CPrioritizedClipSetStreamer::FindBucketIndex(atHashWithStringNotFinal hMemoryGroup) const
{
//Ensure the memory group is valid.
if(hMemoryGroup.IsNull())
{
return -1;
}
//Iterate over the buckets.
int iCount = m_aBuckets.GetCount();
for(int i = 0; i < iCount; ++i)
{
//Ensure the memory group matches.
const CPrioritizedClipSetBucket* pBucket = m_aBuckets[i];
if(hMemoryGroup != pBucket->GetMemoryGroup())
{
continue;
}
return i;
}
return -1;
}
atHashString CPrioritizedClipSetStreamer::GetMemoryGroup(fwMvClipSetId clipSetId) const
{
//Ensure the clip set is valid.
fwClipSet* pClipSet = fwClipSetManager::GetClipSet(clipSetId);
if(!streamPrioritizedVerifyf(pClipSet, "The clip set: %s is invalid.", clipSetId.GetCStr()))
{
return s_hDefaultMemoryGroup;
}
//Ensure the metadata is valid.
fwClipDictionaryMetadata* pClipDictionaryMetadata = pClipSet->GetClipDictionaryMetadata();
return pClipDictionaryMetadata ? pClipDictionaryMetadata->GetMemoryGroup() : s_hDefaultMemoryGroup;
}
void CPrioritizedClipSetStreamer::InitializeBuckets()
{
//Iterate over the memory groups.
int iCount = fwClipSetManager::GetMemoryGroupMetadataCount();
for(int i = 0; i < iCount; ++i)
{
//Ensure the memory group metadata is valid.
fwMemoryGroupMetadata* pMemoryGroupMetadata = fwClipSetManager::GetMemoryGroupMetadataByIndex(i);
if(!pMemoryGroupMetadata)
{
continue;
}
//Ensure the memory group is valid.
atHashString* pMemoryGroup = fwClipSetManager::GetMemoryGroupNameByIndex(i);
if(!pMemoryGroup)
{
continue;
}
//Create the bucket.
CreateBucket(*pMemoryGroup);
}
}
void CPrioritizedClipSetStreamer::InitializeMemoryCosts()
{
//Count the clip dictionaries.
int iMaxCount = fwAnimManager::GetMaxSize();
int iCount = fwAnimManager::GetSize();
//Clear the flags.
m_IsClipDictionaryCountedAgainstMemoryCost.Init(iMaxCount);
m_IsClipDictionaryCountedAgainstMemoryCost.Reset(0);
//Iterate over the clip dictionaries.
for(int i = 0; i < iCount; ++i)
{
//Ensure the slot is valid.
if(!fwAnimManager::IsValidSlot(strLocalIndex(i)))
{
continue;
}
//Update the memory cost for the clip dictionary.
UpdateMemoryCostForClipDictionary(strLocalIndex(i));
}
}
void CPrioritizedClipSetStreamer::OnSituationChanged()
{
//Apply the memory budget adjustments.
ApplyMemoryBudgetAdjustments();
}
void CPrioritizedClipSetStreamer::RandomizeTiebreakers()
{
//Iterate over the buckets.
int iCount = m_aBuckets.GetCount();
for(int i = 0; i < iCount; ++i)
{
//Grab the bucket.
CPrioritizedClipSetBucket* pBucket = m_aBuckets[i];
//Randomize the tiebreakers.
pBucket->RandomizeTiebreakers();
}
//Clear the time since we randomized the tiebreakers.
m_fTimeSinceLastTiebreakersRandomization = 0.0f;
}
bool CPrioritizedClipSetStreamer::ShouldRandomizeTiebreakers() const
{
//Ensure the timer has exceeded the threshold.
static float s_fTimeBetweenTiebreakersRandomizations = 30.0f;
if(m_fTimeSinceLastTiebreakersRandomization < s_fTimeBetweenTiebreakersRandomizations)
{
return false;
}
return true;
}
void CPrioritizedClipSetStreamer::UpdateBuckets()
{
//Check if we should randomize the tiebreakers.
if(ShouldRandomizeTiebreakers())
{
//Randomize the tiebreakers.
RandomizeTiebreakers();
}
//Iterate over the buckets.
int iCount = m_aBuckets.GetCount();
for(int i = 0; i < iCount; ++i)
{
//Update the bucket.
m_aBuckets[i]->Update();
}
}
void CPrioritizedClipSetStreamer::UpdateMemoryCostForClipDictionary(strLocalIndex iIndex)
{
//Ensure we are not patching clip sets. This is an epic hack due to how the clip set patcher works.
//Clip sets can be loaded in before their metadata is available, causing a memory group mismatch when cost is added/removed.
//The clip set cost should be picked up when UpdateMemoryCosts is called.
if(fwClipSetManager::IsPatchingClipSets())
{
return;
}
//Assert that the index is valid.
animAssertf(fwAnimManager::IsValidSlot(iIndex), "The index is invalid: %d.", iIndex.Get());
//Ensure the 'count against cost' state is changing.
bool bShouldCountAgainstCost = ShouldClipDictionaryCountAgainstMemoryCost(iIndex);
bool bIsCountedAgainstCost = IsClipDictionaryCountedAgainstMemoryCost(iIndex.Get());
if(bShouldCountAgainstCost == bIsCountedAgainstCost)
{
return;
}
//Get the memory group for the clip dictionary.
atHashWithStringNotFinal hMemoryGroup = GetMemoryGroupForClipDictionary(iIndex);
//Ensure the bucket is valid.
CPrioritizedClipSetBucket* pBucket = FindBucket(hMemoryGroup);
if(!streamPrioritizedVerifyf(pBucket, "The bucket is invalid for clip dictionary: %s with memory group: %s.", atHashWithStringNotFinal(fwAnimManager::FindHashKeyFromSlot(iIndex)).GetCStr(), hMemoryGroup.GetCStr()))
{
return;
}
//Calculate the cost.
u32 uCost = CalculateMemoryCostForClipDictionary(iIndex);
//Check if the clip dictionary should count against the cost.
if(bShouldCountAgainstCost)
{
//Increase the memory usage.
pBucket->IncreaseMemoryUsage(uCost);
streamPrioritizedDebugf2("Increased memory usage by: %d for bucket: %s resulting in a usage of: %d due to index: %d.", uCost, pBucket->GetMemoryGroup().TryGetCStr(), pBucket->GetMemoryUsage(), iIndex.Get());
//Set the flag.
m_IsClipDictionaryCountedAgainstMemoryCost.Set(iIndex.Get());
}
else
{
//Decrease the memory usage.
pBucket->DecreaseMemoryUsage(uCost);
streamPrioritizedDebugf2("Decreased memory usage by: %d for bucket: %s resulting in a usage of: %d due to index: %d.", uCost, pBucket->GetMemoryGroup().TryGetCStr(), pBucket->GetMemoryUsage(), iIndex.Get());
//Clear the flag.
m_IsClipDictionaryCountedAgainstMemoryCost.Clear(iIndex.Get());
}
}
void CPrioritizedClipSetStreamer::UpdateMemoryCosts()
{
//Note: This function was added as a bit of a safety net for the clip dictionary callback system
// not working 100% as intended. In theory, the callback system should track when clip dictionaries
// are streamed in and out, and gain/lose references. Unfortunately, there are situations when this
// is not the case. One particular situation is when clips add/remove a reference to their owning clip
// dictionary, without going through the clip dictionary store.
//Grab the clip dictionary count.
int iCount = fwAnimManager::GetSize();
//Iterate over a few of the clip dictionaries per frame.
static const int s_iNumClipDictionariesToCheckPerFrame = 50;
for(int i = 0; i < s_iNumClipDictionariesToCheckPerFrame; ++i)
{
//Calculate the index.
strLocalIndex iIndex = strLocalIndex(m_iClipDictionaryIndexToCheck);
m_iClipDictionaryIndexToCheck = (m_iClipDictionaryIndexToCheck + 1) % iCount;
//Ensure the slot is valid.
if(!fwAnimManager::IsValidSlot(iIndex))
{
continue;
}
//Update the memory cost for the clip dictionary.
UpdateMemoryCostForClipDictionary(iIndex);
}
}
void CPrioritizedClipSetStreamer::UpdateSituation()
{
//Ensure we can update the situation.
if(!CanUpdateSituation())
{
return;
}
//Calculate the situation.
atHashWithStringNotFinal hSituation = CalculateSituation();
//Ensure the situation is changing.
if(hSituation == m_hSituation)
{
return;
}
//Assign the situation.
m_hSituation = hSituation;
//Note that the situation changed.
OnSituationChanged();
}
void CPrioritizedClipSetStreamer::UpdateTimers(float fTimeStep)
{
//Update the timers.
m_fTimeSinceLastTiebreakersRandomization += fTimeStep;
}
////////////////////////////////////////////////////////////////////////////////
// CPrioritizedClipSetRequestManager
////////////////////////////////////////////////////////////////////////////////
bool CPrioritizedClipSetRequestManager::ms_SortRequests = true;
#if __DEV
u32 CPrioritizedClipSetRequestManager::ms_TotalRequestAllocationSize = 0;
u32 CPrioritizedClipSetRequestManager::ms_TotalHelperAllocationSize = 0;
#endif // __DEV
#if __BANK
bool CPrioritizedClipSetRequestManager::ms_bSpewDetailedRequests = false;
bool CPrioritizedClipSetRequestManager::ms_bSpewDetailedRequests2 = false;
#endif // __BANK
// Statics
CPrioritizedClipSetRequestManager::Tunables CPrioritizedClipSetRequestManager::ms_Tunables;
IMPLEMENT_VEHICLE_TASK_TUNABLES(CPrioritizedClipSetRequestManager, 0x061c3aa1);
CPrioritizedClipSetRequestManager* CPrioritizedClipSetRequestManager::ms_Instance = NULL;
#if DEBUG_DRAW
const char* CPrioritizedClipSetRequestManager::szContextNames[] = { REQUEST_CONTEXTS(DEFINE_STRINGS_FROM_LIST) };
#endif // DEBUG_DRAW
////////////////////////////////////////////////////////////////////////////////
CPrioritizedClipSetRequestManager::CPrioritizedClipSetRequestManager()
: m_aClipSetRequests()
, m_aClipSetHelpers()
{
}
////////////////////////////////////////////////////////////////////////////////
CPrioritizedClipSetRequestManager::~CPrioritizedClipSetRequestManager()
{
}
////////////////////////////////////////////////////////////////////////////////
#if DEBUG_DRAW
void CPrioritizedClipSetRequestManager::sRequest::RenderDebug(const Vector2& vPosition, int& iLine, bool bClipSetLoaded) const
{
//Create a buffer.
char buf[512];
//Draw the title.
atString streamingPriority;
eStreamingPriority_ToString(priority, streamingPriority);
formatf(buf, "ClipSet: %s, Total Requests: %i, Lifetime: %.2f, Priority: %s", clipsetId.GetCStr(), iNumRequests, fRequestLifeTime, streamingPriority.c_str());
grcDebugDraw::PrintToScreenCoors(buf, (s32)vPosition.x, (s32)vPosition.y + iLine++, bClipSetLoaded ? Color_green : Color_orange);
}
////////////////////////////////////////////////////////////////////////////////
void CPrioritizedClipSetRequestManager::RenderHelperDebug(const Vector2& vPosition, int& iLine, const CPrioritizedClipSetRequestHelper& helper) const
{
//Create a buffer.
char buf[512];
//Draw the title.
formatf(buf, "ClipSet Requested: %s", helper.GetClipSetId().GetCStr());
grcDebugDraw::PrintToScreenCoors(buf, (s32)vPosition.x, (s32)vPosition.y + iLine++, Color_purple);
}
#endif // DEBUG_DRAW
////////////////////////////////////////////////////////////////////////////////
void CPrioritizedClipSetRequestManager::Init(unsigned UNUSED_PARAM(initMode))
{
//Create the instance.
Assert(!ms_Instance);
ms_Instance = rage_new CPrioritizedClipSetRequestManager;
//Initialize the streamer.
ms_Instance->Init();
}
////////////////////////////////////////////////////////////////////////////////
void CPrioritizedClipSetRequestManager::Shutdown(unsigned UNUSED_PARAM(shutdownMode))
{
if (ms_Instance)
{
ms_Instance->Shutdown();
delete ms_Instance;
ms_Instance = NULL;
}
}
////////////////////////////////////////////////////////////////////////////////
bool CPrioritizedClipSetRequestManager::Compare(const sRequest* pRequest, const sRequest* pOtherRequest)
{
return pRequest->clipsetId.GetHash() < pOtherRequest->clipsetId.GetHash();
}
////////////////////////////////////////////////////////////////////////////////
void CPrioritizedClipSetRequestManager::Init()
{
// For each context
for (s32 context=0; context<RC_Max; ++context)
{
m_aClipSetRequests.Push(sClipSetRequests());
m_aClipSetHelpers.Push(sClipSetHelpers());
}
}
////////////////////////////////////////////////////////////////////////////////
void CPrioritizedClipSetRequestManager::Shutdown()
{
// For each context
for (s32 context=0; context<RC_Max; ++context)
{
for(int request = 0; request<m_aClipSetRequests[context].aRequests.GetCount(); ++request)
{
delete m_aClipSetRequests[context].aRequests[request];
}
for(int helper = 0; helper<m_aClipSetHelpers[context].aPrioritisedClipSetRequestHelpers.GetCount(); ++helper)
{
delete m_aClipSetHelpers[context].aPrioritisedClipSetRequestHelpers[helper];
}
}
}
////////////////////////////////////////////////////////////////////////////////
bool CPrioritizedClipSetRequestManager::ShouldClipSetBeStreamedOut(eRequestContext context, fwMvClipSetId clipSetId)
{
s32 iExistingRequestHelperIndex = GetRequestHelperIndexIfExists(context, clipSetId);
if (iExistingRequestHelperIndex > -1)
{
return m_aClipSetHelpers[context].aPrioritisedClipSetRequestHelpers[iExistingRequestHelperIndex]->ShouldClipSetBeStreamedOut();
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
void CPrioritizedClipSetRequestManager::UpdateRequest(sRequest& rOldRequest, const sRequest& rNewRequest)
{
// Use the highest lifetime
if (rNewRequest.fRequestLifeTime > rOldRequest.fRequestLifeTime)
{
rOldRequest.fRequestLifeTime = rNewRequest.fRequestLifeTime;
}
// Always keep high priority requests
if (rNewRequest.priority == SP_High)
{
rOldRequest.priority = SP_High;
}
}
////////////////////////////////////////////////////////////////////////////////
bool CPrioritizedClipSetRequestManager::Request(sRequest& request, bool bCheckLoaded)
{
#if __DEV
ms_TotalRequestAllocationSize += sizeof(sRequest);
#endif // __DEV
bool bAddedRequest = false;
if (aiVerifyf(request.clipsetId != CLIP_SET_ID_INVALID, "Clipset requested is invalid"))
{
if (aiVerifyf(m_aClipSetRequests[request.context].aRequests.GetCount() < ms_Tunables.m_MaxNumRequestsPerContext, "Trying to request more than %i clipsets for context %s, check request calls", ms_Tunables.m_MaxNumRequestsPerContext, szContextNames[request.context]))
{
m_aClipSetRequests[request.context].aRequests.PushAndGrow(&request);
bAddedRequest = true;
if (ms_SortRequests)
{
SortRequests(request.context);
}
}
}
if (!bAddedRequest)
{
delete &request;
}
#if __BANK
TUNE_GROUP_BOOL(STREAMING_TUNE, ENABLE_GET_CLIPSET_OPTIMIZATION, true);
TUNE_GROUP_BOOL(STREAMING_TUNE, ENABLE_FIND_REQUEST_OPTIMIZATION, true);
if (!ENABLE_FIND_REQUEST_OPTIMIZATION)
{
ms_SortRequests = false;
}
if (bCheckLoaded || !ENABLE_GET_CLIPSET_OPTIMIZATION)
#else
if (bCheckLoaded)
#endif // __BANK
{
fwClipSet* pClipSet = fwClipSetManager::GetClipSet(request.clipsetId);
if (pClipSet)
{
return pClipSet->IsStreamedIn_DEPRECATED();
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool CPrioritizedClipSetRequestManager::IsLoaded(fwMvClipSetId clipSetId)
{
fwClipSet* pClipSet = fwClipSetManager::GetClipSet(clipSetId);
if (aiVerifyf(pClipSet, "No clipset found for clipsetid %s", clipSetId.GetCStr()))
{
return pClipSet->IsStreamedIn_DEPRECATED();
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
void CPrioritizedClipSetRequestManager::Update(float fTimeStep)
{
#if __BANK
if (ms_bSpewDetailedRequests)
{
streamPrioritizedDebugf1("Frame : %i, CPrioritizedClipSetRequestManager::Update called", fwTimer::GetFrameCount());
}
#endif // __BANK
// For each context
for (s32 context=0; context<RC_Max; ++context)
{
// Score and sort requests
//ScoreAndSortRequests((eRequestContext)context);
// Remove duplicate clipset requests (TODO: keep highest scoring ones)
//RemoveDuplicatesFromSortedArray((eRequestContext)context);
#if __DEV
CheckForDuplicates((eRequestContext)context);
#endif // __DEV
// Update the lifetimes of the requests and remove any requests/helpers that have expired
ProcessRequests((eRequestContext)context, fTimeStep);
// Add a priority clipset helper if it doesn't already exist and request the clipset
AddRequestHelperIfNoneExists((eRequestContext)context);
// TODO: Alter priorities based on scores
}
}
////////////////////////////////////////////////////////////////////////////////
#if DEBUG_DRAW
void CPrioritizedClipSetRequestManager::RenderDebug()
{
if (!ms_Tunables.m_RenderDebugDraw)
return;
//Initialize the position.
static Vector2 s_vInitialPosition(0.0f, 0.0f);
Vector2 vPosition(s_vInitialPosition);
vPosition += ms_Tunables.m_vScroll;
//Keep track of the line.
int iLine = 0;
//Create a buffer.
char buf[512];
#if __DEV
formatf(buf, "Total Request Allocations : %u", ms_TotalRequestAllocationSize);
grcDebugDraw::PrintToScreenCoors(buf, (s32)vPosition.x, (s32)vPosition.y + iLine++, Color_white);
iLine++;
formatf(buf, "Total Helper Allocations : %u", ms_TotalHelperAllocationSize);
grcDebugDraw::PrintToScreenCoors(buf, (s32)vPosition.x, (s32)vPosition.y + iLine++, Color_white);
iLine++;
iLine++;
#endif // __DEV
//Draw the title.
formatf(buf, "Prioritized ClipSet Request Manager");
grcDebugDraw::PrintToScreenCoors(buf, (s32)vPosition.x, (s32)vPosition.y + iLine++, Color_cyan);
iLine++;
vPosition.x += ms_Tunables.m_fIndent;
//Iterate over the requests for each context
for(int i = 0; i < RC_Max; ++i)
{
int iRequestCount = m_aClipSetRequests[i].aRequests.GetCount();
Vector2 vContextPosition = vPosition;
vContextPosition.x += ms_Tunables.m_fIndent;
formatf(buf, "Context : %s", szContextNames[i]);
grcDebugDraw::PrintToScreenCoors(buf, (s32)vContextPosition.x, (s32)vPosition.y + iLine++, Color_blue);
formatf(buf, "ClipSet Requests (%i)", iRequestCount);
grcDebugDraw::PrintToScreenCoors(buf, (s32)vContextPosition.x, (s32)vPosition.y + iLine++, Color_white);
Vector2 vRequestPosition = vContextPosition;
vRequestPosition.x += ms_Tunables.m_fIndent;
for(int request = 0; request< iRequestCount; ++request)
{
//Grab the request.
const sRequest& rRequest = *m_aClipSetRequests[i].aRequests[request];
//Render the debug text for the request.
rRequest.RenderDebug(vRequestPosition, iLine, IsLoaded(rRequest.clipsetId));
}
int iHelperCount = m_aClipSetHelpers[i].aPrioritisedClipSetRequestHelpers.GetCount();
formatf(buf, "Priority ClipSet Helpers (%i)", iHelperCount);
grcDebugDraw::PrintToScreenCoors(buf, (s32)vContextPosition.x, (s32)vPosition.y + iLine++, Color_white);
Vector2 vHelperPosition = vContextPosition;
vHelperPosition.x += ms_Tunables.m_fIndent;
for(int helper = 0; helper< iHelperCount; ++helper)
{
//Grab the helper.
const CPrioritizedClipSetRequestHelper& rHelper = *m_aClipSetHelpers[i].aPrioritisedClipSetRequestHelpers[helper];
//Render the debug text for the helper.
RenderHelperDebug(vHelperPosition, iLine, rHelper);
}
}
}
#endif // DEBUG_DRAW
////////////////////////////////////////////////////////////////////////////////
// void CPrioritizedClipSetRequestManager::ScoreAndSortRequests(eRequestContext context)
// {
// // Ensure the request count is valid.
// s32 iCount = m_aClipSetRequests[context].aRequests.GetCount();
// if (iCount <= 1)
// {
// return;
// }
//
// // TODO: Score requests based on:
// // - Distance/Angle to current gameplayer camera
// // - Whether requesting entity is a player/mission ped?
// // - Requested priority
// // ScoreRequests();
//
// // Sort the requests by clipset hash so its easier to remove dupes
// sRequest** pRequests = &m_aClipSetRequests[context].aRequests[0];
// std::sort(pRequests, pRequests + iCount, CPrioritizedClipSetRequestManager::Compare);
// }
//
// ////////////////////////////////////////////////////////////////////////////////
//
// void CPrioritizedClipSetRequestManager::RemoveDuplicatesFromSortedArray(eRequestContext context)
// {
// const s32 iTotalNumElements = m_aClipSetRequests[context].aRequests.GetCount();
// if (iTotalNumElements > 1)
// {
// // Count the number of unique elements, our array should be sized to it once we've removed dupes
// u32 uNumUniqueElements = 0;
//
// // Go through the sorted array, comparing each element with the first unique element not already processed (uNumUniqueElements)
// // if the other request isn't a dupe, its a unique element so we have finished processing this element and move onto the next
// // We update the unique element we're processing with the details of current element we're accessing
// // Once we've looped through the array we should have removed all duplicates and uNumUniqueElements will hold the required
// // new size of the array.
// s32 iNumDuplicatesFound = 0;
//
// for (s32 i=1; i<iTotalNumElements; ++i)
// {
// if (!IsOtherRequestADupe(context, uNumUniqueElements, i))
// {
// iNumDuplicatesFound = 0;
// ++uNumUniqueElements;
// }
// else
// {
// ++iNumDuplicatesFound;
// }
// CopyDetailsToUniqueElement(context, uNumUniqueElements, i, iNumDuplicatesFound);
// }
//
// // Add one since we start at zero
// ++uNumUniqueElements;
//
// // If we found at least one dupe, go through all the dupes and delete them
// if (uNumUniqueElements < iTotalNumElements)
// {
// while (m_aClipSetRequests[context].aRequests.GetCount() > uNumUniqueElements)
// {
// #if __DEV
// ms_TotalRequestAllocationSize -= sizeof(sRequest);
// #endif // __DEV
// delete m_aClipSetRequests[context].aRequests[uNumUniqueElements];
// m_aClipSetRequests[context].aRequests.Delete(uNumUniqueElements);
// }
// }
//
// streamPrioritizedAssertf(uNumUniqueElements == (u32)m_aClipSetRequests[context].aRequests.GetCount(), "Remove duplicates failed, Num Unique Elements : %i, Num Elements : %i", uNumUniqueElements, m_aClipSetRequests[context].aRequests.GetCount());
// }
// }
////////////////////////////////////////////////////////////////////////////////
#if __DEV
void CPrioritizedClipSetRequestManager::CheckForDuplicates(eRequestContext context) const
{
const s32 iTotalNumElements = m_aClipSetRequests[context].aRequests.GetCount();
if (iTotalNumElements > 1)
{
// Count the number of unique elements, our array should be sized to it once we've removed dupes
u32 uNumUniqueElements = 0;
// Go through the sorted array, comparing each element with the first unique element not already processed (uNumUniqueElements)
// if the other request isn't a dupe, its a unique element so we have finished processing this element and move onto the next
s32 iNumDuplicatesFound = 0;
for (s32 i=1; i<iTotalNumElements; ++i)
{
if (!IsOtherRequestADupe(context, uNumUniqueElements, i))
{
iNumDuplicatesFound = 0;
++uNumUniqueElements;
}
else
{
streamPrioritizedAssertf(!IsOtherRequestADupe(context, uNumUniqueElements, i), "Duplicate request of ClipSet %s found", m_aClipSetRequests[context].aRequests[uNumUniqueElements]->clipsetId.GetCStr());
++iNumDuplicatesFound;
}
}
}
}
#endif // __DEV
////////////////////////////////////////////////////////////////////////////////
bool CPrioritizedClipSetRequestManager::IsOtherRequestADupe(eRequestContext context, u32 uCurrentUniqueElementIndex, s32 iOtherRequestIndex) const
{
sRequest& rCurrentRequest = *m_aClipSetRequests[context].aRequests[uCurrentUniqueElementIndex];
sRequest& rOtherRequest = *m_aClipSetRequests[context].aRequests[iOtherRequestIndex];
if (rCurrentRequest.clipsetId == rOtherRequest.clipsetId)
{
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
void CPrioritizedClipSetRequestManager::CopyDetailsToUniqueElement(eRequestContext context, u32 uCurrentUniqueElementIndex, s32 iUniqueRequestIndex, s32 iNumDuplicatesFound)
{
sRequest& rCurrentRequest = *m_aClipSetRequests[context].aRequests[uCurrentUniqueElementIndex];
sRequest& rOtherRequest = *m_aClipSetRequests[context].aRequests[iUniqueRequestIndex];
// This is the first instance, copy all details
if (iNumDuplicatesFound == 0)
{
rCurrentRequest = rOtherRequest;
}
else
{
UpdateRequest(rCurrentRequest, rOtherRequest);
rCurrentRequest.iNumRequests = iNumDuplicatesFound;
}
}
////////////////////////////////////////////////////////////////////////////////
fwClipSet* CPrioritizedClipSetRequestManager::GetClipSet(fwMvClipSetId clipSetId) const
{
//Ensure the clip set is valid.
if(!streamPrioritizedVerifyf(clipSetId != CLIP_SET_ID_INVALID, "The clip set is invalid."))
{
return NULL;
}
return fwClipSetManager::GetClipSet(clipSetId);
}
////////////////////////////////////////////////////////////////////////////////
bool CPrioritizedClipSetRequestManager::Request(eRequestContext context, fwMvClipSetId clipSetId, eStreamingPriority priority, float fLifeTime, bool bCheckLoaded)
{
streamPrioritizedAssertf(clipSetId != CLIP_SET_ID_INVALID, "Invalid clipset passed in");
// If there's already an existing request, just update it with a request created on the stack, otherwise allocate a new request
s32 iExistingRequestIndex = CPrioritizedClipSetRequestManager::GetInstance().GetRequestIndexIfExists(context, clipSetId);
if (iExistingRequestIndex > -1)
{
CPrioritizedClipSetRequestManager::GetInstance().UpdateRequest(context, iExistingRequestIndex, CPrioritizedClipSetRequestManager::sRequest(clipSetId, context, priority, fLifeTime > -1.0f ? fLifeTime : 1.0f));
return bCheckLoaded ? CPrioritizedClipSetRequestManager::GetInstance().IsLoaded(clipSetId) : false;
}
else
{
return CPrioritizedClipSetRequestManager::GetInstance().Request(*rage_new CPrioritizedClipSetRequestManager::sRequest(clipSetId, context, priority, fLifeTime > -1.0f ? fLifeTime : 1.0f), bCheckLoaded);
}
}
////////////////////////////////////////////////////////////////////////////////
s32 CPrioritizedClipSetRequestManager::GetRequestIndexIfExists(eRequestContext context, fwMvClipSetId clipSetId)
{
const s32 iTotalNumElements = m_aClipSetRequests[context].aRequests.GetCount();
if (iTotalNumElements > 0)
{
TUNE_GROUP_INT(CLIPSET_STREAMING_TUNE, MIN_CLIPSETS_TO_BINARY_SEARCH, 20, 0, 100, 1);
if (ms_SortRequests && iTotalNumElements > MIN_CLIPSETS_TO_BINARY_SEARCH)
{
// m_aClipSetRequests is a sorted array, and we use
// std::lower_bound() to do a binary search here.
sRequest key;
key.clipsetId = clipSetId;
sRequest** pRequests = &m_aClipSetRequests[context].aRequests[0];
sRequest** found = std::lower_bound(pRequests, pRequests+iTotalNumElements, &key, CPrioritizedClipSetRequestManager::Compare);
const int index = (int)(found - pRequests);
if (index >= 0 && index < iTotalNumElements)
{
return m_aClipSetRequests[context].aRequests[index]->clipsetId == clipSetId ? index : -1;
}
}
else
{
for (s32 i=0; i<iTotalNumElements; ++i)
{
if (m_aClipSetRequests[context].aRequests[i]->clipsetId == clipSetId)
{
return i;
}
}
}
}
return -1;
}
////////////////////////////////////////////////////////////////////////////////
bool CPrioritizedClipSetRequestManager::HasClipSetBeenRequested(eRequestContext context, fwMvClipSetId clipSetId)
{
return GetRequestIndexIfExists(context,clipSetId) >= 0;
}
////////////////////////////////////////////////////////////////////////////////
#if __BANK
const CPrioritizedClipSetRequestHelper* CPrioritizedClipSetRequestManager::GetPrioritizedClipSetRequestHelperForClipSet(eRequestContext context, fwMvClipSetId clipSetId)
{
s32 iRequestHelperIndex = GetRequestHelperIndexIfExists(context, clipSetId);
if (iRequestHelperIndex > -1)
{
return m_aClipSetHelpers[context].aPrioritisedClipSetRequestHelpers[iRequestHelperIndex];
}
return NULL;
}
#endif // __BANK
////////////////////////////////////////////////////////////////////////////////
s32 CPrioritizedClipSetRequestManager::GetRequestHelperIndexIfExists(eRequestContext context, fwMvClipSetId clipSetId)
{
const atArray<CPrioritizedClipSetRequestHelper*>& clipSetRequestHelperArray = m_aClipSetHelpers[context].aPrioritisedClipSetRequestHelpers;
for (s32 i=0; i<clipSetRequestHelperArray.GetCount(); ++i)
{
if (clipSetRequestHelperArray[i]->GetClipSetId() == clipSetId)
{
return i;
}
}
return -1;
}
////////////////////////////////////////////////////////////////////////////////
void CPrioritizedClipSetRequestManager::ProcessRequests(eRequestContext context, float fTimeStep)
{
if (m_aClipSetRequests[context].aRequests.GetCount() > 0)
{
// Go through each request (from end to start) and update the life time
for (s32 request=m_aClipSetRequests[context].aRequests.GetCount()-1; request>=0; --request)
{
m_aClipSetRequests[context].aRequests[request]->fRequestLifeTime -= fTimeStep;
// Request has expired, so remove
if (m_aClipSetRequests[context].aRequests[request]->fRequestLifeTime < 0.0f)
{
#if __BANK
if (ms_bSpewDetailedRequests)
{
streamPrioritizedDebugf1("Frame : %i, request expired clipset %s", fwTimer::GetFrameCount(), m_aClipSetRequests[context].aRequests[request]->clipsetId.GetCStr());
}
#endif // __BANK
#if __DEV
ms_TotalRequestAllocationSize -= sizeof(sRequest);
#endif // __DEV
// Delete the request
delete m_aClipSetRequests[context].aRequests[request];
// Remove the request from the array
m_aClipSetRequests[context].aRequests.Delete(request);
}
#if __BANK
else
{
if (ms_bSpewDetailedRequests)
{
streamPrioritizedDebugf1("Frame : %i, valid request found clipset %s", fwTimer::GetFrameCount(), m_aClipSetRequests[context].aRequests[request]->clipsetId.GetCStr());
}
}
#endif // __BANK
}
}
if (m_aClipSetHelpers[context].aPrioritisedClipSetRequestHelpers.GetCount() > 0)
{
// Go through each helper (from end to start) and see if we have a corresponding request for that clipset
// we remove the helper if we don't have a request for that clipset
for (s32 helper=m_aClipSetHelpers[context].aPrioritisedClipSetRequestHelpers.GetCount()-1; helper>=0; --helper)
{
fwMvClipSetId clipSetId = m_aClipSetHelpers[context].aPrioritisedClipSetRequestHelpers[helper]->GetClipSetId();
if (!FindRequestWithClipSetId(context, clipSetId))
{
#if __BANK
if (ms_bSpewDetailedRequests)
{
streamPrioritizedDebugf1("Frame : %i, removing clipset helper for clipset %s because no requests for that clipset exist", fwTimer::GetFrameCount(), clipSetId.GetCStr());
}
#endif // __BANK
#if __DEV
ms_TotalHelperAllocationSize -= sizeof(CPrioritizedClipSetRequestHelper);
#endif // __DEV
// Delete the prioritised clipset helper
delete m_aClipSetHelpers[context].aPrioritisedClipSetRequestHelpers[helper];
// Remove from the array
m_aClipSetHelpers[context].aPrioritisedClipSetRequestHelpers.Delete(helper);
}
#if __BANK
else
{
if (ms_bSpewDetailedRequests)
{
streamPrioritizedDebugf1("Frame : %i, found request for clipset %s, keeping clipset helper", fwTimer::GetFrameCount(), clipSetId.GetCStr());
}
}
#endif // __BANK
}
}
}
////////////////////////////////////////////////////////////////////////////////
void CPrioritizedClipSetRequestManager::AddRequestHelperIfNoneExists(eRequestContext context)
{
if (m_aClipSetRequests[context].aRequests.GetCount() > 0)
{
// Go through each request
for (s32 request=0; request<m_aClipSetRequests[context].aRequests.GetCount(); ++request)
{
CPrioritizedClipSetRequestHelper* pPriorityRequest = NULL;
s32 iRequestHelperIndex = GetRequestHelperIndexIfExists((eRequestContext)context, m_aClipSetRequests[context].aRequests[request]->clipsetId);
// Add a clipset helper if one doesn't exist
if (iRequestHelperIndex == -1)
{
#if __BANK
if (ms_bSpewDetailedRequests)
{
streamPrioritizedDebugf1("Frame : %i, couldn't find request for clipset %s", fwTimer::GetFrameCount(), m_aClipSetRequests[context].aRequests[request]->clipsetId.GetCStr());
}
#endif // __BANK
#if __DEV
ms_TotalHelperAllocationSize += sizeof(CPrioritizedClipSetRequestHelper);
#endif // __DEV
pPriorityRequest = rage_new CPrioritizedClipSetRequestHelper();
pPriorityRequest->RequestClipSet(m_aClipSetRequests[context].aRequests[request]->clipsetId);
m_aClipSetHelpers[context].aPrioritisedClipSetRequestHelpers.PushAndGrow(pPriorityRequest);
}
else
{
pPriorityRequest = m_aClipSetHelpers[context].aPrioritisedClipSetRequestHelpers[iRequestHelperIndex];
}
// Force the request to be high priority if specified
if (pPriorityRequest)
{
#if __BANK
// Verify the request is valid
streamPrioritizedAssertf(pPriorityRequest->GetClipSetId() == m_aClipSetRequests[context].aRequests[request]->clipsetId, "Clipset (%s) in priority request doesn't match the clipset in request helper (%s), Tez would like to see this", pPriorityRequest->GetClipSetId().GetCStr(), m_aClipSetRequests[context].aRequests[request]->clipsetId.GetCStr());
#endif // __BANK
eStreamingPriority desiredPriority = m_aClipSetRequests[context].aRequests[request]->priority;
#if __BANK
if (ms_bSpewDetailedRequests)
{
const CPrioritizedClipSetRequest* pRequest = pPriorityRequest->GetPrioritizedClipSetRequest();
streamPrioritizedDebugf1("Frame : %i, found request for clipset %s, priority (desired:%i, normal:%i, override:%i) , stream status %i", fwTimer::GetFrameCount(), m_aClipSetRequests[context].aRequests[request]->clipsetId.GetCStr(), (s32)(desiredPriority), (s32)(pRequest->GetStreamingPriority()), (s32)(pRequest->GetStreamingPriorityOverride()), (s32)(pRequest->GetDesiredStreamingState()));
}
#endif // __BANK
if (desiredPriority != SP_Invalid)
{
pPriorityRequest->SetStreamingPriorityOverride(desiredPriority);
}
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
s32 CPrioritizedClipSetRequestManager::FindRequestWithClipSetId(eRequestContext context, fwMvClipSetId clipSetId)
{
if (m_aClipSetRequests[context].aRequests.GetCount() > 0)
{
// Go through each request and see if it matches the clipsetid passed in
for (s32 request=0; request<m_aClipSetRequests[context].aRequests.GetCount(); ++request)
{
if (m_aClipSetRequests[context].aRequests[request]->clipsetId == clipSetId)
{
return true;
}
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
void CPrioritizedClipSetRequestManager::SortRequests(eRequestContext context)
{
// Ensure the request count is valid.
s32 iCount = m_aClipSetRequests[context].aRequests.GetCount();
if (iCount <= 1)
{
return;
}
// Sort the requests by clipset hash so its easier to remove dupes
sRequest** pRequests = &m_aClipSetRequests[context].aRequests[0];
std::sort(pRequests, pRequests + iCount, CPrioritizedClipSetRequestManager::Compare);
}
////////////////////////////////////////////////////////////////////////////////