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

453 lines
13 KiB
C++

#include "Frontend/Store/StoreTextureManager.h"
#include "Frontend/Store/StoreUIChannel.h"
#include "Frontend/Store/StoreScreenMgr.h"
#include "renderer/RenderThread.h"
#include "scene/DownloadableTextureManager.h"
#include "system/memory.h"
FRONTEND_STORE_OPTIMISATIONS()
const int COMMERCE_TEXTURE_SIZE = 256 * 1024;
const int FRAMES_TO_DELAY_REF_REMOVAL = 3;
const int INITIAL_REQUEST_HASH_ARRAY_SIZE = 20;
cStoreTextureManager::cStoreTextureManager() :
m_pMemoryAllocator(NULL),
m_CurrentState(STATE_NEUTRAL),
m_CurrentRequestHandle(CTextureDownloadRequest::INVALID_HANDLE)
{
m_RequestedTextureHashArray.Reset();
m_RequestedTextureHashArray.Reserve(INITIAL_REQUEST_HASH_ARRAY_SIZE);
}
cStoreTextureManager::~cStoreTextureManager()
{
Shutdown();
}
void cStoreTextureManager::Init()
{
m_RequestedImagesQueue.Reset();
m_CurrentTextureTitle.Reset();
m_CurrentState = STATE_NEUTRAL;
m_RequestedTextureHashArray.Reset();
m_RequestedTextureHashArray.Reserve(INITIAL_REQUEST_HASH_ARRAY_SIZE);
}
void cStoreTextureManager::Shutdown()
{
m_RequestedImagesQueue.Reset();
m_CurrentTextureTitle.Reset();
m_CurrentState = STATE_NEUTRAL;
FreeAllTextures();
if ( m_CurrentRequestHandle != CTextureDownloadRequest::INVALID_HANDLE )
{
DOWNLOADABLETEXTUREMGR.ReleaseTextureDownloadRequest( m_CurrentRequestHandle );
m_CurrentRequestHandle = CTextureDownloadRequest::INVALID_HANDLE;
}
}
void cStoreTextureManager::Update()
{
switch( m_CurrentState )
{
case(STATE_NEUTRAL):
UpdateNeutralState();
break;
case(STATE_DOWNLOADING):
UpdateDownloadingState();
break;
case(STATE_ERROR):
UpdateErrorState();
break;
default:
storeUIAssertf(false,"Unhandled state in m_CurrentState");
}
UpdateGarbage();
}
bool cStoreTextureManager::RequestTexture( const atString& aTexturePath )
{
atString strippedPath = aTexturePath;
strippedPath.Replace("cloud:/","");
//Check to see if this texture is either on the way or queued.
if ( m_RequestedImagesQueue.Find( strippedPath ) || m_CurrentTextureTitle == strippedPath )
{
return false;
}
cStoreTexture **existingStoreTexture = m_TextureMap.Access( strippedPath );
if ( existingStoreTexture )
{
//This has been downloaded and registered already.
(*existingStoreTexture)->IncRefCount();
return true;
}
//Add to the DL list.
storeUIDebugf3("Added %s to the cStoreTextureManager download queue.",strippedPath.c_str());
AddToDownloadQueue( strippedPath );
return false;
}
void cStoreTextureManager::AddToDownloadQueue( const char *aTexturePath )
{
atString texturePathString(aTexturePath);
if ( m_RequestedImagesQueue.Find( texturePathString ) )
{
return;
}
if ( m_RequestedImagesQueue.GetCount() == MAX_QUEUED_IMAGES )
{
storeUIErrorf("Downloadable image queue is full.");
return;
}
storeUIDebugf3("Adding %s to the requested image queue.", aTexturePath );
m_RequestedImagesQueue.Push( texturePathString );
}
void cStoreTextureManager::UpdateNeutralState()
{
//If the queue is not empty, start a download and go to downloading state.
if ( m_RequestedImagesQueue.GetCount() > 0 )
{
m_CurrentTextureTitle = m_RequestedImagesQueue.Pop();
if ( StartTextureDownload( m_CurrentTextureTitle ) )
{
m_CurrentState = STATE_DOWNLOADING;
}
}
}
void cStoreTextureManager::UpdateDownloadingState()
{
storeUIAssert( m_CurrentRequestHandle != CTextureDownloadRequest::INVALID_HANDLE );
if ( DOWNLOADABLETEXTUREMGR.HasRequestFailed(m_CurrentRequestHandle) )
{
storeUIErrorf( "Fetch of path %s failed.", m_CurrentTextureTitle.c_str() );
m_CurrentState = STATE_NEUTRAL;
}
else if ( DOWNLOADABLETEXTUREMGR.IsRequestReady( m_CurrentRequestHandle ) )
{
if ( !CreateTexture() )
{
//This means we have been asked to wait a frame.
return;
}
m_CurrentTextureTitle.Reset();
DOWNLOADABLETEXTUREMGR.ReleaseTextureDownloadRequest( m_CurrentRequestHandle );
m_CurrentRequestHandle = CTextureDownloadRequest::INVALID_HANDLE;
m_CurrentState = STATE_NEUTRAL;
}
}
void cStoreTextureManager::UpdateErrorState()
{
//An error has occurred. Check whether it is terminal, and report then set back to neutral as appropriate
}
int cStoreTextureManager::GetSizeOfRequestedTexture( const char* /*aTexturePath*/ )
{
//Do this in a dumb way at first. May smarten this up moving forward.
return COMMERCE_TEXTURE_SIZE;
}
bool cStoreTextureManager::StartTextureDownload( const char *aPath )
{
int sizeInBytes = GetSizeOfRequestedTexture( aPath );
CTextureDownloadRequestDesc::Type requestType = CTextureDownloadRequestDesc::TITLE_FILE;
atString strippedName(aPath);
ConvertPathToTextureName( strippedName );
// Fill in the descriptor for our request
CTextureDownloadRequestDesc requestDesc;
requestDesc.m_Type = requestType;
requestDesc.m_GamerIndex = -1;
requestDesc.m_CloudFilePath = aPath;
requestDesc.m_BufferPresize = sizeInBytes;
requestDesc.m_TxtAndTextureHash = strippedName.c_str();
if ( m_RequestedTextureHashArray.Find( atStringHash(aPath)) == -1 )
{
m_RequestedTextureHashArray.Append() = atStringHash(aPath);
}
else
{
//Previously requested
requestDesc.m_CloudRequestFlags = eRequest_CacheForceCache; //TODO_ANDI: not right
}
// Issue the request
TextureDownloadRequestHandle handle;
CTextureDownloadRequest::Status retVal;
retVal = DOWNLOADABLETEXTUREMGR.RequestTextureDownload(handle, requestDesc);
// Check the return value
if (retVal != CTextureDownloadRequest::IN_PROGRESS && retVal != CTextureDownloadRequest::READY_FOR_USER )
{
storeUIWarningf("cStoreTextureManager::StartTextureDownload: request failed before being issued");
return false;
}
// Cache the handle for querying the state of the request
m_CurrentRequestHandle = handle;
return true;
}
bool cStoreTextureManager::CreateTexture()
{
cStoreTexture* storeTexture = rage_new cStoreTexture( m_CurrentTextureTitle );
storeUIAssert(DOWNLOADABLETEXTUREMGR.IsRequestReady(m_CurrentRequestHandle));
int txdSlot = DOWNLOADABLETEXTUREMGR.GetRequestTxdSlot(m_CurrentRequestHandle).Get();
if ( !storeTexture->InitialiseTexture(txdSlot) )
{
//handle the "try next frame" state.
return false;
}
m_TextureMap.Insert( m_CurrentTextureTitle, storeTexture );
return true;
}
void cStoreTextureManager::ConvertPathToTextureName( atString &aPath )
{
aPath.Replace("cloud:/","");
aPath.Replace("/","__");
aPath.Uppercase();
aPath.Replace(".DDS","");
}
void cStoreTextureManager::ReleaseTexture( const atString& aTexturePath )
{
atString strippedPath = aTexturePath;
strippedPath.Replace("cloud:/","");
//See if the texture is already in the map
cStoreTexture **retrievedTexture = m_TextureMap.Access(strippedPath);
cStoreTexture *storeTexture;
if (retrievedTexture == NULL)
{
//No surch texture
storeUIErrorf("Requested the release of a texture that does not exist: %s", strippedPath.c_str());
return;
}
storeTexture = *retrievedTexture;
storeTexture->DecRefCount();
if ( storeTexture->GetRefCount() <= 0 )
{
storeUIDebugf3( "Destroying %s with a ref count of %d", storeTexture->GetTextureName().c_str(), storeTexture->GetRefCount() );
storeTexture->Destroy();
m_TextureMap.Delete(strippedPath);
delete storeTexture;
}
}
void cStoreTextureManager::PutInGarbage( int txdSlot )
{
// Defer deletion for three frames. This seems to be the agreed safe time from drawListMgr.
TextureGarbageNode* pGarbageNode = rage_new TextureGarbageNode( TextureGarbageEntry( txdSlot, FRAMES_TO_DELAY_REF_REMOVAL ) );
m_TextureGarbage.Append( *pGarbageNode );
}
void cStoreTextureManager::UpdateGarbage()
{
// Loop over the garbage map and look for entries that need to be deleted
TextureGarbageNode* pGarbageNode = m_TextureGarbage.GetHead();
while ( NULL != pGarbageNode )
{
TextureGarbageNode* pTempNode = pGarbageNode;
pGarbageNode = pGarbageNode->GetNext();
if ( pTempNode->Data.DestroyDeferred() )
{
m_TextureGarbage.Remove( *pTempNode );
delete pTempNode;
}
}
}
void cStoreTextureManager::FreeAllTextures()
{
// Flush the render thread, we don't want to delete textures out from under it
gRenderThreadInterface.Flush();
// Destroy any textures we had
atMap<atString,cStoreTexture*>::Iterator storeTextureIter = m_TextureMap.CreateIterator();
for (storeTextureIter.Start(); !storeTextureIter.AtEnd(); ++storeTextureIter)
{
cStoreTexture* pTexture = storeTextureIter.GetData();
delete pTexture;
}
m_TextureMap.Kill();
// Destroy any textures that were sitting in the garbage
TextureGarbageNode* pTextureNode = m_TextureGarbage.GetHead();
while ( NULL != pTextureNode )
{
pTextureNode->Data.DestroyImmediate();
pTextureNode = pTextureNode->GetNext();
}
m_TextureGarbage.DeleteAll();
}
void cStoreTextureManager::CancelRequest( const char* aTexturePath )
{
atString strippedPath(aTexturePath);
strippedPath.Replace("cloud:/","");
int findIndex = -1;
if ( m_RequestedImagesQueue.Find( strippedPath, &findIndex ) )
{
m_RequestedImagesQueue.Delete(findIndex);
}
if ( m_CurrentTextureTitle == strippedPath )
{
DOWNLOADABLETEXTUREMGR.ReleaseTextureDownloadRequest(m_CurrentRequestHandle);
m_CurrentRequestHandle = CTextureDownloadRequest::INVALID_HANDLE;
storeUIErrorf( "Fetch of path %s canceled.", m_CurrentTextureTitle.c_str() );
m_CurrentState = STATE_NEUTRAL;
}
}
cStoreTextureManager::cStoreTexture::cStoreTexture( atString textureName ):
m_TextureDictionarySlot(-1),
m_TextureName(textureName),
m_RefCount( 0 ),
m_JustCreated(true)
{
}
cStoreTextureManager::cStoreTexture::~cStoreTexture()
{
Destroy();
}
bool cStoreTextureManager::cStoreTexture::InitialiseTexture( int downloadedTxdSlot )
{
m_TextureDictionarySlot = downloadedTxdSlot;
RegisterAsTxd();
return true;
}
void cStoreTextureManager::cStoreTexture::Destroy()
{
if ( m_TextureDictionarySlot.Get() != -1 )
{
storeUIAssert(cStoreScreenMgr::GetTextureManager());
cStoreScreenMgr::GetTextureManager()->PutInGarbage( m_TextureDictionarySlot.Get() );
m_TextureDictionarySlot = strLocalIndex(-1);
}
}
bool cStoreTextureManager::cStoreTexture::RegisterAsTxd()
{
if(!storeUIVerifyf(m_TextureDictionarySlot != -1 , "Invalid TXD to register slot for emblem"))
{
return false;
}
atString strippedName = m_TextureName;
cStoreTextureManager::ConvertPathToTextureName( strippedName );
strLocalIndex txdSlot = m_TextureDictionarySlot;
if (storeUIVerifyf(g_TxdStore.IsValidSlot(txdSlot), "cStoreTextureManager::cStoreTexture::RegisterAsTxd() - failed to get valid txd for slot %d", txdSlot.Get()))
{
fwTxd* txd = g_TxdStore.GetSafeFromIndex(txdSlot);
if (txd)
{
g_TxdStore.AddRef(txdSlot, REF_OTHER);
storeUIDebugf3("Added Texture '%s' to TXD '%s' at slot %d", strippedName.c_str(), strippedName.c_str(), txdSlot.Get());
return true;
}
}
return false;
}
void cStoreTextureManager::cStoreTexture::UnregisterAsTxd()
{
//This is now handled by the garbage collector
/*
//check to see if the slot is already there.
if (m_TextureDictionarySlot != -1)
{
g_TxdStore.RemoveRef(m_TextureDictionarySlot, REF_OTHER);
}
m_TextureDictionarySlot = -1;
*/
}
bool cStoreTextureManager::TextureGarbageEntry::DestroyDeferred()
{
--m_nNumFrames;
if ( m_nNumFrames <= 0 )
{
storeUIDisplayf( "Deleting texture slot %d in cStoreTextureManager::TextureGarbageEntry::DestroyDeferred\n", m_TxdSlot.Get());
DestroyImmediate();
return true;
}
return false;
}
void cStoreTextureManager::TextureGarbageEntry::DestroyImmediate()
{
if ( m_TxdSlot.Get() )
{
storeUIDisplayf( "Deleting texture <%d> (actually just decreasing ref) in cStoreTextureManager::TextureGarbageEntry::DestroyImmediate\n", m_TxdSlot.Get() );
g_TxdStore.RemoveRef(m_TxdSlot, REF_OTHER);
m_TxdSlot = -1;
}
}