453 lines
13 KiB
C++
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;
|
|
}
|
|
} |