// 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(static_cast(*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(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(static_cast(*this).FindBucket(clipSetId)); } const CPrioritizedClipSetBucket* CPrioritizedClipSetStreamer::FindBucket(fwMvClipSetId clipSetId) const { return (FindBucket(GetMemoryGroup(clipSetId))); } CPrioritizedClipSetBucket* CPrioritizedClipSetStreamer::FindBucket(atHashWithStringNotFinal hMemoryGroup) { return const_cast(static_cast(*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 -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 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 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; iclipsetId.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; iclipsetId == 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& clipSetRequestHelperArray = m_aClipSetHelpers[context].aPrioritisedClipSetRequestHelpers; for (s32 i=0; iGetClipSetId() == 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; requestclipsetId); // 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; requestclipsetId == 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); } ////////////////////////////////////////////////////////////////////////////////