//============================================================================= // Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. // // File: loadingmanager.cpp // // Description: Implementation for the LoadingManager class. // // History: + Created -- Darwin Chau // //============================================================================= //======================================== // System Includes //======================================== // Ftech #include #include #include #include //======================================== // Project Includes //======================================== #include #include #include #include #include #include #include
#include #include //****************************************************************************** // // Global Data, Local Data, Local Classes // //****************************************************************************** // Static pointer to instance of singleton. LoadingManager* LoadingManager::spInstance = NULL; //****************************************************************************** // // Public Member Functions // //****************************************************************************** //============================================================================== // LoadingManager::GetInstance //============================================================================== // // Description: Access point for the LoadingManager singleton. // // Parameters: None. // // Return: Pointer to the LoadingManager. // // Constraints: This is a singleton so only one instance is allowed. // //============================================================================== LoadingManager* LoadingManager::GetInstance() { rAssert( spInstance != NULL ); return spInstance; } //============================================================================== // LoadingManager::CreateInstance //============================================================================== // // Description: Constructs the LoadingManager singleton. // // Parameters: None. // // Return: Pointer to the LoadingManager. // // Constraints: This is a singleton so only one instance is allowed. // //============================================================================== LoadingManager* LoadingManager::CreateInstance() { MEMTRACK_PUSH_GROUP( "LoadingManager" ); rAssert( spInstance == NULL ); #ifdef RAD_GAMECUBE HeapMgr()->PushHeap( GMA_GC_VMM ); #else HeapMgr()->PushHeap( GMA_PERSISTENT ); #endif spInstance = new LoadingManager; rAssert( spInstance ); #ifdef RAD_GAMECUBE HeapMgr()->PopHeap( GMA_GC_VMM ); #else HeapMgr()->PopHeap( GMA_PERSISTENT ); #endif MEMTRACK_POP_GROUP("LoadingManager"); return spInstance; } //============================================================================== // LoadingManager::DestroyInstance //============================================================================== // // Description: Destroy the LoadingManager. // // Parameters: None. // // Return: None. // //============================================================================== void LoadingManager::DestroyInstance() { rAssert( spInstance != NULL ); delete spInstance; spInstance = NULL; } void LoadingManager::AddCallback( LoadingManager::ProcessRequestsCallback* pCallback, void* pUserData) { if ( mCancellingLoads ) { return; } if(mRequestHead == mRequestTail && !mLoading ) { pCallback->OnProcessRequestsComplete(pUserData); return; } int newTail = (mRequestTail + 1) % MAX_REQUESTS; rAssert( newTail != mRequestHead ); //rAssert(!mRequests[lastAddedRequest].pCallback); mRequests[mRequestTail].pCallback = pCallback; mRequests[mRequestTail].pUserData = pUserData; mRequests[mRequestTail].filename[0] = '\0'; mRequestTail = newTail; } //============================================================================== // LoadingManager::AddRequest //============================================================================== // // Description: Clients use this method to submit a loading request. // This request is not serviced immediately. The request remains // queued until LoadingManager::ProcessRequests() is invoked. // // Parameters: handlerType - an enumeration is used to specify which // file handler to use for loading and processing the data // (the use of an enumeration keeps the clients of LoadingManager // from being dependant on any FileHandler classes. // // filename - fully qualified path and name of file. // // secitonName - hack to allow p3d inventory sections to be created // based on filenames // // groupName - this allows a memtag section to be declared. // // Return: true - on success // false - request queue is full or loading is in progress // //============================================================================== void LoadingManager::AddRequest ( FileHandlerEnum handlerType, const char* filename, GameMemoryAllocator heap, const char* sectionName, const char* groupTag, LoadingManager::ProcessRequestsCallback* pCallback, void* pUserData ) { if ( mCancellingLoads ) { return; } unsigned newTail = (mRequestTail + 1) % MAX_REQUESTS; #ifndef FINAL rReleaseAssertMsg( static_cast< int >( newTail ) != mRequestHead, "Too many load requests already!\n"); // Dusit [Dec 2, 2002]: // Bad if we're overwriting the other load request. This is a fatal error. if( static_cast(newTail) == mRequestHead ) { IRadTextDisplay* textDisplay; ::radTextDisplayGet( &textDisplay, GMA_DEFAULT ); if ( textDisplay ) { textDisplay->SetBackgroundColor( 0 ); textDisplay->SetTextColor( 0xffffffff ); textDisplay->Clear(); textDisplay->TextOutAt( "TOO MANY LOAD REQUESTS!", 20, 10 ); textDisplay->TextOutAt( ">:-8", 20, 14 ); if ( CommandLineOptions::Get( CLO_DEMO_TEST ) || GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_DEMO_TEST ) ) { char buffy[32]; sprintf( buffy, "Demo Count: %d", GetGame()->GetDemoCount() ); textDisplay->TextOutAt( buffy, 20, 16 ); unsigned int time = GetGame()->GetTime(); unsigned int hours = time / 3600000; unsigned int deltaTime = time % 3600000; unsigned int minutes = deltaTime / 60000; deltaTime = deltaTime % 60000; unsigned int seconds = deltaTime / 1000; deltaTime = deltaTime % 1000; sprintf( buffy, "Time: %d:%d:%d.%d", hours, minutes, seconds, deltaTime ); textDisplay->TextOutAt( buffy, 20, 18 ); if ( GetGameplayManager() ) { sprintf( buffy, "Level %d", GetGameplayManager()->GetCurrentLevelIndex() ); textDisplay->TextOutAt( buffy, 20, 20 ); } } textDisplay->SwapBuffers(); textDisplay->Release(); } rReleaseBreak(); } #endif //mRequests[mNumRequests].filename = filename; rAssert( strlen( filename ) < LoadingRequest::LOADING_FILENAME_LENGTH ); strcpy( mRequests[mRequestTail].filename, filename ); mRequests[mRequestTail].pFileHandler = FileHandlerFactory::CreateFileHandler( handlerType, sectionName ); mRequests[mRequestTail].pFileHandler->AddRef(); #ifdef MEMORYTRACKER_ENABLED if( groupTag == NULL ) { // // Use the filename by default // rAssert( strlen( filename ) < LoadingRequest::LOADING_FILENAME_LENGTH ); strcpy( mRequests[mRequestTail].groupTag, filename ); } else { rAssert( strlen( groupTag ) < LoadingRequest::LOADING_FILENAME_LENGTH ); strcpy( mRequests[mRequestTail].groupTag, groupTag ); } #endif mRequests[mRequestTail].heap = heap; mRequests[mRequestTail].pCallback = pCallback; mRequests[mRequestTail].pUserData = pUserData; mRequestTail = newTail; this->ProcessNextRequest(); } //============================================================================== // LoadingManager::OnLoadFileComplete //============================================================================== // // Description: FileHandler's will invoke this callback when an asynchronous // load completes. // // Parameters: pUserData - optional user data that was passed in when the // load was requested // // Return: None. // //============================================================================== void LoadingManager::OnLoadFileComplete( void* pUserData ) { rAssert( (int)pUserData == mRequestHead ); // Display some debug info. LoadingRequest& request = mRequests[mRequestHead ]; extern bool gLoadingSpew; if ( !(CommandLineOptions::Get( CLO_NO_LOADING_SPEW )) ) { rReleasePrintf( "<< END >> Async Loading: %s (%u msecs)\n", request.filename, radTimeGetMilliseconds() - request.startTime ); } // Continue onto the next request. mRequestHead = (mRequestHead + 1) % MAX_REQUESTS; mLoading = false; if(request.pCallback) { request.pCallback->OnProcessRequestsComplete(request.pUserData); } request.pFileHandler->Release(); this->ProcessNextRequest(); } //============================================================================== // LoadingManager::LoadSync //============================================================================== // // Description: Load a file synchronously. // // Parameters: handlerType - an enumeration is used to specify which // file handler to use for loading and processing the data // (the use of an enumeration keeps the clients of LoadingManager // from being dependant on any FileHandler classes. // // filename - fully qualified path and name of file. // // Return: None. // //============================================================================== void LoadingManager::LoadSync ( FileHandlerEnum handlerType, const char* filename, GameMemoryAllocator heap, const char* sectionName ) { rReleasePrintf("\n\n!!!!!! TRC VIOLATION, USE ASYNC!!!!!!!\n\n"); // rAssert( false ); unsigned int startTime = radTimeGetMilliseconds(); if ( !(CommandLineOptions::Get( CLO_NO_LOADING_SPEW )) ) { rDebugPrintf( "<> Sync Loading: %s\n", filename ); } HeapMgr()->PushHeap( GMA_TEMP ); FileHandler* pHandler = FileHandlerFactory::CreateFileHandler( handlerType, sectionName ); HeapMgr()->PopHeap( GMA_TEMP ); pHandler->AddRef( ); rAssert( pHandler ); HeapMgr()->PushHeap( heap ); pHandler->LoadFileSync( filename ); HeapMgr()->PopHeap( heap ); pHandler->Release( ); pHandler = 0; if ( !(CommandLineOptions::Get( CLO_NO_LOADING_SPEW )) ) { rReleasePrintf( "<< END >> Sync Loading: %s (%u msecs)\n", filename, radTimeGetMilliseconds() - startTime ); } } //============================================================================= // LoadingManager::CancelPendingRequests //============================================================================= // Description: Comment // // Parameters: () // // Return: void // //============================================================================= void LoadingManager::CancelPendingRequests() { if ( mRequestHead != mRequestTail ) { mRequestTail = (mRequestHead + 1) % MAX_REQUESTS; //Catch up and lose the rest of the loads. } mCancellingLoads = true; while ( mLoading ) { ::radFileService(); p3d::loadManager->SwitchTask(); p3d::loadManager->TriggerCallbacks(); } //Close all the open cement files. CementFileHandle i; for ( i = 0; i < MAX_CEMENT_LIBRARIES; ++i ) { if ( mCementLibraries[ i ].library != NULL ) { UnregisterCementLibrary( i ); } } } //============================================================================= // LoadingManager::RegisterCementLibrary //============================================================================= // Description: Registers the named cement library with RadFile. // // Parameters: filename - name of cement file // // Return: CementFileHandle - internally, its the index of the cement // library pointer. Used for indicating which // cement library to release later on // //============================================================================= CementFileHandle LoadingManager::RegisterCementLibrary( const char* filename ) { if ( mCancellingLoads ) { return 0; } int i; for( i = 0; i < MAX_CEMENT_LIBRARIES; i++ ) { if( ( mCementLibraries[i].library == NULL ) && ( mCementLibraries[i].isLoading == false ) ) { break; } } rAssertMsg( ( i < MAX_CEMENT_LIBRARIES ), "Too many cement libraries\n" ); // // Put a file handler into the queue so that we'll wait until this // thing is fully registered. This file handler is slightly different // than the rest, and we're in a special-purpose function anyway, so don't // go through the factory. // int newTail = (mRequestTail + 1) % MAX_REQUESTS; if( newTail != mRequestHead) { rAssert( strlen( filename ) < LoadingRequest::LOADING_FILENAME_LENGTH ); strcpy( mRequests[mRequestTail].filename, filename ); #ifdef MEMORYTRACKER_ENABLED strcpy( mRequests[mRequestTail].groupTag, filename ); #endif #ifdef RAD_GAMECUBE mRequests[mRequestTail].heap = GMA_GC_VMM; #else mRequests[mRequestTail].heap = GMA_PERSISTENT; // Cement library registrations should be around for the duration --jdy #endif MEMTRACK_PUSH_GROUP( "LoadingManager" ); HeapMgr()->PushHeap (GMA_TEMP); mRequests[mRequestTail].pFileHandler = new(GMA_TEMP) CementFileHandler( &(mCementLibraries[i]) ); mRequests[mRequestTail].pFileHandler->AddRef(); mCementLibraries[i].isLoading = true; HeapMgr()->PopHeap (GMA_TEMP); MEMTRACK_POP_GROUP("LoadingManager"); mRequests[mRequestTail].pCallback = NULL; mRequestTail = newTail; this->ProcessNextRequest(); return( i ); } else { rWarningMsg( 0, "Failed to add cement library request" ); return( -1 ); } } //============================================================================= // LoadingManager::UnregisterCementLibrary //============================================================================= // Description: Unregister a cement library (hence the name) // // Parameters: handle - in practice, the array index for the cement library // // Return: void // //============================================================================= void LoadingManager::UnregisterCementLibrary( CementFileHandle handle ) { rAssert( handle >= 0 ); //rAssert( mCementLibraries[handle].library != NULL ); // // Just to be safe, make sure that the cement library is inactive before // we release it. Synchronous wait okay here? // if( mCementLibraries[handle].library != NULL ) { mCementLibraries[handle].library->WaitForCompletion(); mCementLibraries[handle].library->Release(); mCementLibraries[handle].library = NULL; } } //****************************************************************************** // // Private Member Functions // //****************************************************************************** //============================================================================== // LoadingManager::LoadingManager //============================================================================== // // Description: Constructor. // // Parameters: None. // // Return: N/A. // //============================================================================== LoadingManager::LoadingManager() : mRequestHead( 0 ), mRequestTail( 0 ), mLoading(false), mCancellingLoads( false ) { int i; for( i = 0; i < MAX_CEMENT_LIBRARIES; i++ ) { mCementLibraries[i].library = NULL; mCementLibraries[i].isLoading = false; } #ifdef RAD_WIN32 mRequestsProcessed = 0; #endif } //============================================================================== // LoadingManager::~LoadingManager //============================================================================== // // Description: Destructor. // // Parameters: None. // // Return: N/A. // //============================================================================== LoadingManager::~LoadingManager() { int i; for( i = 0; i < MAX_CEMENT_LIBRARIES; i++ ) { if( mCementLibraries[i].library != NULL ) { mCementLibraries[i].library->Release(); } } } //============================================================================== // LoadingManager::ProcessNextRequest //============================================================================== // // Description: Handle the next load request in the queue. // // Parameters: None. // // Return: None. // //============================================================================== void LoadingManager::ProcessNextRequest() { // Are we done? if(!mLoading) { if(mRequestHead != mRequestTail) { // Start loading the next file. LoadingRequest& request = mRequests[mRequestHead]; if ( request.filename[0] != '\0' ) { if ( !(CommandLineOptions::Get( CLO_NO_LOADING_SPEW )) ) { rReleasePrintf( "<> Async Loading: %s\n", request.filename ); } #ifdef RAD_WIN32 mRequestsProcessed++; #endif request.startTime = radTimeGetMilliseconds(); mLoading = true; GameMemoryAllocator heap = request.heap; #ifndef RAD_RELEASE if (HeapManager::s_bSpecialRoute) { heap = GMA_SPECIAL; } #endif request.pFileHandler->LoadFile( request.filename, this, (void*)mRequestHead, heap ); } else { rAssert( request.pCallback != NULL ); mRequestHead = (mRequestHead + 1) % MAX_REQUESTS; mLoading = false; request.pCallback->OnProcessRequestsComplete(request.pUserData); ProcessNextRequest(); } } } }